From 540122e7031452bc8b8e38791f86e2b94122d050 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 24 Dec 2017 12:41:52 +0100 Subject: [PATCH 001/118] gobj: fix crash with ShaderBuffer --- panda/src/gobj/shaderBuffer.I | 6 ++++-- panda/src/gobj/shaderBuffer.cxx | 8 ++++++++ panda/src/gobj/shaderBuffer.h | 2 ++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/panda/src/gobj/shaderBuffer.I b/panda/src/gobj/shaderBuffer.I index eb51f42f4e..fbd0b42e46 100644 --- a/panda/src/gobj/shaderBuffer.I +++ b/panda/src/gobj/shaderBuffer.I @@ -19,7 +19,8 @@ INLINE ShaderBuffer:: ShaderBuffer(const string &name, uint64_t size, UsageHint usage_hint) : Namable(name), _data_size_bytes(size), - _usage_hint(usage_hint) { + _usage_hint(usage_hint), + _contexts(nullptr) { } /** @@ -31,7 +32,8 @@ ShaderBuffer(const string &name, pvector initial_data, UsageHint Namable(name), _data_size_bytes(initial_data.size()), _usage_hint(usage_hint), - _initial_data(initial_data) { + _initial_data(initial_data), + _contexts(nullptr) { } /** diff --git a/panda/src/gobj/shaderBuffer.cxx b/panda/src/gobj/shaderBuffer.cxx index a7cd5f8150..f0b45df540 100644 --- a/panda/src/gobj/shaderBuffer.cxx +++ b/panda/src/gobj/shaderBuffer.cxx @@ -16,6 +16,14 @@ TypeHandle ShaderBuffer::_type_handle; +/** + * Destructor. + */ +ShaderBuffer:: +~ShaderBuffer() { + release_all(); +} + /** * */ diff --git a/panda/src/gobj/shaderBuffer.h b/panda/src/gobj/shaderBuffer.h index f5fe50dbc1..8d48fed425 100644 --- a/panda/src/gobj/shaderBuffer.h +++ b/panda/src/gobj/shaderBuffer.h @@ -32,6 +32,8 @@ private: INLINE ShaderBuffer() DEFAULT_CTOR; PUBLISHED: + ~ShaderBuffer(); + INLINE explicit ShaderBuffer(const string &name, uint64_t size, UsageHint usage_hint); INLINE explicit ShaderBuffer(const string &name, pvector initial_data, UsageHint usage_hint); From 0281f306e1ef2478310934be634c8d3568bcb88e Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 24 Dec 2017 15:09:21 +0100 Subject: [PATCH 002/118] express: add PointerToArray.count() --- panda/src/express/pointerToArray.I | 26 ++++++++++++++++++++++++++ panda/src/express/pointerToArray.h | 8 ++++++++ panda/src/linmath/lmatrix4_src.I | 16 ++++++++++++++++ panda/src/linmath/lmatrix4_src.h | 3 +++ panda/src/linmath/lvecBase4_src.I | 19 +++++++++++++++++++ panda/src/linmath/lvecBase4_src.h | 3 +++ 6 files changed, 75 insertions(+) diff --git a/panda/src/express/pointerToArray.I b/panda/src/express/pointerToArray.I index 7bf8b89d4e..c3a10f3737 100644 --- a/panda/src/express/pointerToArray.I +++ b/panda/src/express/pointerToArray.I @@ -581,6 +581,19 @@ node_unref() const { return ((To *)(this->_void_ptr))->node_unref(); } +/** + * Counts the frequency at which the given element occurs in the vector. + */ +template +INLINE size_t PointerToArray:: +count(const Element &value) const { + if ((this->_void_ptr) != nullptr) { + return std::count(begin(), end(), value); + } else { + return 0; + } +} + /** * */ @@ -1006,6 +1019,19 @@ node_unref() const { return ((To *)(this->_void_ptr))->node_unref(); } +/** + * Counts the frequency at which the given element occurs in the vector. + */ +template +INLINE size_t ConstPointerToArray:: +count(const Element &value) const { + if ((this->_void_ptr) != nullptr) { + return std::count(begin(), end(), value); + } else { + return 0; + } +} + /** * */ diff --git a/panda/src/express/pointerToArray.h b/panda/src/express/pointerToArray.h index e3dcc2f110..762a6cb8ce 100644 --- a/panda/src/express/pointerToArray.h +++ b/panda/src/express/pointerToArray.h @@ -112,6 +112,8 @@ PUBLISHED: INLINE int get_ref_count() const; INLINE int get_node_ref_count() const; + INLINE size_t count(const Element &) const; + #ifdef HAVE_PYTHON EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags)); EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const); @@ -214,6 +216,8 @@ public: INLINE void node_ref() const; INLINE bool node_unref() const; + INLINE size_t count(const Element &) const; + // Reassignment is by pointer, not memberwise as with a vector. INLINE PointerToArray & operator = (ReferenceCountedVector *ptr); @@ -266,6 +270,8 @@ PUBLISHED: INLINE int get_ref_count() const; INLINE int get_node_ref_count() const; + INLINE size_t count(const Element &) const; + #ifdef HAVE_PYTHON EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const); EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const); @@ -341,6 +347,8 @@ PUBLISHED: INLINE void node_ref() const; INLINE bool node_unref() const; + INLINE size_t count(const Element &) const; + // Reassignment is by pointer, not memberwise as with a vector. INLINE ConstPointerToArray & operator = (ReferenceCountedVector *ptr); diff --git a/panda/src/linmath/lmatrix4_src.I b/panda/src/linmath/lmatrix4_src.I index 32250339c3..d5c16d5ce7 100644 --- a/panda/src/linmath/lmatrix4_src.I +++ b/panda/src/linmath/lmatrix4_src.I @@ -1706,3 +1706,19 @@ INLINE_LINMATH int FLOATNAME(UnalignedLMatrix4):: get_num_components() const { return 16; } + +/** + * + */ +INLINE_LINMATH bool FLOATNAME(UnalignedLMatrix4):: +operator == (const FLOATNAME(UnalignedLMatrix4) &other) const { + return memcmp(get_data(), other.get_data(), sizeof(FLOATTYPE) * 16) == 0; +} + +/** + * + */ +INLINE_LINMATH bool FLOATNAME(UnalignedLMatrix4):: +operator != (const FLOATNAME(UnalignedLMatrix4) &other) const { + return !operator == (other); +} diff --git a/panda/src/linmath/lmatrix4_src.h b/panda/src/linmath/lmatrix4_src.h index 0b9f1557e5..de2d2fbf78 100644 --- a/panda/src/linmath/lmatrix4_src.h +++ b/panda/src/linmath/lmatrix4_src.h @@ -345,6 +345,9 @@ PUBLISHED: INLINE_LINMATH const FLOATTYPE *get_data() const; INLINE_LINMATH int get_num_components() const; + INLINE_LINMATH bool operator == (const FLOATNAME(UnalignedLMatrix4) &other) const; + INLINE_LINMATH bool operator != (const FLOATNAME(UnalignedLMatrix4) &other) const; + public: typedef UNALIGNED_LINMATH_MATRIX(FLOATTYPE, 4, 4) UMatrix4; UMatrix4 _m; diff --git a/panda/src/linmath/lvecBase4_src.I b/panda/src/linmath/lvecBase4_src.I index 464cd6fa12..bdf7e917b6 100644 --- a/panda/src/linmath/lvecBase4_src.I +++ b/panda/src/linmath/lvecBase4_src.I @@ -971,3 +971,22 @@ INLINE_LINMATH const FLOATTYPE *FLOATNAME(UnalignedLVecBase4):: get_data() const { return &_v(0); } + +/** + * + */ +INLINE_LINMATH bool FLOATNAME(UnalignedLVecBase4):: +operator == (const FLOATNAME(UnalignedLVecBase4) &other) const { + return (_v(0) == other._v(0) && + _v(1) == other._v(1) && + _v(2) == other._v(2) && + _v(3) == other._v(3)); +} + +/** + * + */ +INLINE_LINMATH bool FLOATNAME(UnalignedLVecBase4):: +operator != (const FLOATNAME(UnalignedLVecBase4) &other) const { + return !operator == (other); +} diff --git a/panda/src/linmath/lvecBase4_src.h b/panda/src/linmath/lvecBase4_src.h index fdd81dad4b..d04201d988 100644 --- a/panda/src/linmath/lvecBase4_src.h +++ b/panda/src/linmath/lvecBase4_src.h @@ -243,6 +243,9 @@ PUBLISHED: INLINE_LINMATH const FLOATTYPE *get_data() const; CONSTEXPR static int get_num_components() { return 4; } + INLINE_LINMATH bool operator == (const FLOATNAME(UnalignedLVecBase4) &other) const; + INLINE_LINMATH bool operator != (const FLOATNAME(UnalignedLVecBase4) &other) const; + public: typedef FLOATTYPE numeric_type; typedef UNALIGNED_LINMATH_MATRIX(FLOATTYPE, 1, 4) UVector4; From 588d41ed7e4878427ae9ae0e0d96ac03d34df601 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 24 Dec 2017 15:12:45 +0100 Subject: [PATCH 003/118] Fix assorted compiler warnings --- direct/src/distributed/cConnectionRepository.cxx | 8 ++++---- panda/src/display/graphicsWindowProcCallbackData.I | 4 ++-- panda/src/display/graphicsWindowProcCallbackData.h | 6 +++--- panda/src/display/pythonGraphicsWindowProc.cxx | 2 +- panda/src/dxgsg9/dxGraphicsStateGuardian9.I | 2 +- panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx | 4 ++-- panda/src/dxgsg9/dxTextureContext9.cxx | 2 +- panda/src/dxgsg9/wdxGraphicsWindow9.cxx | 8 ++++---- panda/src/event/asyncFuture.h | 4 ++-- panda/src/movies/flacAudioCursor.cxx | 1 - panda/src/wgldisplay/wglGraphicsWindow.cxx | 2 +- 11 files changed, 21 insertions(+), 22 deletions(-) diff --git a/direct/src/distributed/cConnectionRepository.cxx b/direct/src/distributed/cConnectionRepository.cxx index 1781422930..a19491d825 100644 --- a/direct/src/distributed/cConnectionRepository.cxx +++ b/direct/src/distributed/cConnectionRepository.cxx @@ -708,7 +708,7 @@ handle_update_field() { Py_DECREF(dclass_obj); nassertr(dclass_this != NULL, false); - DCClass *dclass = (DCClass *)PyLong_AsLong(dclass_this); + DCClass *dclass = (DCClass *)PyLong_AsVoidPtr(dclass_this); Py_DECREF(dclass_this); // If in quiet zone mode, throw update away unless distobj has @@ -799,7 +799,7 @@ handle_update_field_owner() { Py_DECREF(dclass_obj); nassertr(dclass_this != NULL, false); - DCClass *dclass = (DCClass *)PyLong_AsLong(dclass_this); + DCClass *dclass = (DCClass *)PyLong_AsVoidPtr(dclass_this); Py_DECREF(dclass_this); // check if we should forward this update to the owner view @@ -841,7 +841,7 @@ handle_update_field_owner() { Py_DECREF(dclass_obj); nassertr(dclass_this != NULL, false); - DCClass *dclass = (DCClass *)PyLong_AsLong(dclass_this); + DCClass *dclass = (DCClass *)PyLong_AsVoidPtr(dclass_this); Py_DECREF(dclass_this); // check if we should forward this update to the owner view @@ -974,7 +974,7 @@ describe_message(ostream &out, const string &prefix, Py_DECREF(dclass_obj); nassertv(dclass_this != NULL); - dclass = (DCClass *)PyLong_AsLong(dclass_this); + dclass = (DCClass *)PyLong_AsVoidPtr(dclass_this); Py_DECREF(dclass_this); } } diff --git a/panda/src/display/graphicsWindowProcCallbackData.I b/panda/src/display/graphicsWindowProcCallbackData.I index 55fc325ad9..eb12340a30 100644 --- a/panda/src/display/graphicsWindowProcCallbackData.I +++ b/panda/src/display/graphicsWindowProcCallbackData.I @@ -32,7 +32,7 @@ get_graphics_window() const { /** * Returns the Windows proc hwnd parameter. */ -INLINE int GraphicsWindowProcCallbackData:: +INLINE uintptr_t GraphicsWindowProcCallbackData:: get_hwnd() const { return _hwnd; } @@ -65,7 +65,7 @@ get_lparam() const { * Sets the Windows proc hwnd parameter. */ INLINE void GraphicsWindowProcCallbackData:: -set_hwnd(int hwnd) { +set_hwnd(uintptr_t hwnd) { _hwnd = hwnd; } diff --git a/panda/src/display/graphicsWindowProcCallbackData.h b/panda/src/display/graphicsWindowProcCallbackData.h index 9e3c747d50..d417a3595e 100644 --- a/panda/src/display/graphicsWindowProcCallbackData.h +++ b/panda/src/display/graphicsWindowProcCallbackData.h @@ -32,7 +32,7 @@ public: INLINE GraphicsWindow* get_graphics_window() const; #ifdef WIN32 - INLINE void set_hwnd(int hwnd); + INLINE void set_hwnd(uintptr_t hwnd); INLINE void set_msg(int msg); INLINE void set_wparam(int wparam); INLINE void set_lparam(int lparam); @@ -42,7 +42,7 @@ PUBLISHED: virtual void output(ostream &out) const; #ifdef WIN32 - INLINE int get_hwnd() const; + INLINE uintptr_t get_hwnd() const; INLINE int get_msg() const; INLINE int get_wparam() const; INLINE int get_lparam() const; @@ -55,7 +55,7 @@ PUBLISHED: private: GraphicsWindow* _graphicsWindow; #ifdef WIN32 - int _hwnd; + uintptr_t _hwnd; int _msg; int _wparam; int _lparam; diff --git a/panda/src/display/pythonGraphicsWindowProc.cxx b/panda/src/display/pythonGraphicsWindowProc.cxx index d114d9ba3d..fe2013bed1 100644 --- a/panda/src/display/pythonGraphicsWindowProc.cxx +++ b/panda/src/display/pythonGraphicsWindowProc.cxx @@ -47,7 +47,7 @@ PythonGraphicsWindowProc:: LONG PythonGraphicsWindowProc:: wnd_proc(GraphicsWindow* graphicsWindow, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam){ GraphicsWindowProcCallbackData cdata(graphicsWindow); - cdata.set_hwnd((int)hwnd); + cdata.set_hwnd((uintptr_t)hwnd); cdata.set_msg(msg); cdata.set_wparam(wparam); cdata.set_lparam(lparam); diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.I b/panda/src/dxgsg9/dxGraphicsStateGuardian9.I index 7b5133ccf4..94e50a5f7d 100644 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.I +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.I @@ -127,7 +127,7 @@ get_safe_buffer_start() { // buffer, and then pointing to the first multiple of 0x10000 within that // buffer. _temp_buffer = new unsigned char[0x1ffff]; - _safe_buffer_start = (unsigned char *)(((long)_temp_buffer + 0xffff) & ~0xffff); + _safe_buffer_start = (unsigned char *)(((uintptr_t)_temp_buffer + 0xffff) & ~0xffff); } return _safe_buffer_start; diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx index b51d67c564..8326068dc1 100644 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx @@ -4936,7 +4936,7 @@ draw_primitive_up(D3DPRIMITIVETYPE primitive_type, _d3d_device->DrawPrimitiveUP(primitive_type, primitive_count, buffer_start, stride); - } else if ((((long)buffer_end ^ (long)buffer_start) & ~0xffff) == 0) { + } else if ((((uintptr_t)buffer_end ^ (uintptr_t)buffer_start) & ~0xffff) == 0) { // No problem; we can draw the buffer directly. _d3d_device->DrawPrimitiveUP(primitive_type, primitive_count, buffer_start, stride); @@ -4978,7 +4978,7 @@ draw_indexed_primitive_up(D3DPRIMITIVETYPE primitive_type, (primitive_type, min_index, max_index - min_index + 1, num_primitives, index_data, index_type, buffer, stride); - } else if ((((long)buffer_end ^ (long)buffer_start) & ~0xffff) == 0) { + } else if ((((uintptr_t)buffer_end ^ (uintptr_t)buffer_start) & ~0xffff) == 0) { // No problem; we can draw the buffer directly. _d3d_device->DrawIndexedPrimitiveUP (primitive_type, min_index, max_index - min_index + 1, num_primitives, diff --git a/panda/src/dxgsg9/dxTextureContext9.cxx b/panda/src/dxgsg9/dxTextureContext9.cxx index 39b92342aa..babfae755e 100644 --- a/panda/src/dxgsg9/dxTextureContext9.cxx +++ b/panda/src/dxgsg9/dxTextureContext9.cxx @@ -680,7 +680,7 @@ create_texture(DXScreenData &scrn) { << "NumColorChannels: " << num_color_channels << "; NumAlphaBits: " << num_alpha_bits << "; targetbpp: " <HighPart) << "." << LOWORD(DrvVer->HighPart) << "." << HIWORD(DrvVer->LowPart) << "." << LOWORD(DrvVer->LowPart) - << ")\nVendorID: 0x" << (void*) adapter_info.VendorId - << " DeviceID: 0x" << (void*) adapter_info.DeviceId - << " SubsysID: 0x" << (void*) adapter_info.SubSysId - << " Revision: 0x" << (void*) adapter_info.Revision << endl; + << ")\nVendorID: 0x" << hex << adapter_info.VendorId + << " DeviceID: 0x" << adapter_info.DeviceId + << " SubsysID: 0x" << adapter_info.SubSysId + << " Revision: 0x" << adapter_info.Revision << dec << endl; HMONITOR _monitor = dxpipe->__d3d9->GetAdapterMonitor(i); if (_monitor == NULL) { diff --git a/panda/src/event/asyncFuture.h b/panda/src/event/asyncFuture.h index e3a835eea3..453384fbb5 100644 --- a/panda/src/event/asyncFuture.h +++ b/panda/src/event/asyncFuture.h @@ -185,10 +185,10 @@ public: register_type(_type_handle, "AsyncGatheringFuture", AsyncFuture::get_class_type()); } - virtual TypeHandle get_type() const { + virtual TypeHandle get_type() const override { return get_class_type(); } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + virtual TypeHandle force_init_type() override {init_type(); return get_class_type();} private: static TypeHandle _type_handle; diff --git a/panda/src/movies/flacAudioCursor.cxx b/panda/src/movies/flacAudioCursor.cxx index 839fc8724c..a4c33822cd 100644 --- a/panda/src/movies/flacAudioCursor.cxx +++ b/panda/src/movies/flacAudioCursor.cxx @@ -17,7 +17,6 @@ #include "config_movies.h" #define DR_FLAC_IMPLEMENTATION -#define DR_FLAC_NO_STDIO extern "C" { #include "dr_flac.h" } diff --git a/panda/src/wgldisplay/wglGraphicsWindow.cxx b/panda/src/wgldisplay/wglGraphicsWindow.cxx index d6fe0799b9..06fc3d3ed4 100644 --- a/panda/src/wgldisplay/wglGraphicsWindow.cxx +++ b/panda/src/wgldisplay/wglGraphicsWindow.cxx @@ -386,7 +386,7 @@ print_pfd(PIXELFORMATDESCRIPTOR *pfd, char *msg) { wgldisplay_cat.debug() << msg << ", " << OGLDrvStrings[drvtype] << " driver\n" - << "PFD flags: 0x" << (void*)pfd->dwFlags << " (" + << "PFD flags: 0x" << hex << pfd->dwFlags << dec << " (" << PRINT_FLAG(GENERIC_ACCELERATED) << PRINT_FLAG(GENERIC_FORMAT) << PRINT_FLAG(DOUBLEBUFFER) From 2f910ccd7ccf0dfae85d0245cd8d8d325978b8ff Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 24 Dec 2017 15:13:19 +0100 Subject: [PATCH 004/118] tests: fixes for the window tests --- tests/display/conftest.py | 15 +++++++++------ tests/display/test_window.py | 5 +++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/display/conftest.py b/tests/display/conftest.py index 821b06af80..09684fab84 100644 --- a/tests/display/conftest.py +++ b/tests/display/conftest.py @@ -1,23 +1,26 @@ import pytest -@pytest.fixture -def graphics_pipe(scope='session'): + +@pytest.fixture(scope='session') +def graphics_pipe(): from panda3d.core import GraphicsPipeSelection pipe = GraphicsPipeSelection.get_global_ptr().make_default_pipe() - if not pipe.is_valid(): - pytest.xfail("GraphicsPipe is invalid") + if pipe is None or not pipe.is_valid(): + pytest.skip("GraphicsPipe is invalid") yield pipe -@pytest.fixture -def graphics_engine(scope='session'): + +@pytest.fixture(scope='session') +def graphics_engine(): from panda3d.core import GraphicsEngine engine = GraphicsEngine.get_global_ptr() yield engine + @pytest.fixture def window(graphics_pipe, graphics_engine): from panda3d.core import GraphicsPipe, FrameBufferProperties, WindowProperties diff --git a/tests/display/test_window.py b/tests/display/test_window.py index d193fe92dd..1c44ea76e3 100644 --- a/tests/display/test_window.py +++ b/tests/display/test_window.py @@ -5,11 +5,12 @@ def test_window_basic(window): current_props = window.get_properties() default_props = WindowProperties.get_default() - # Opening the window changes these from the defaults + # Opening the window changes these from the defaults. Note that we have + # no guarantee that it opens in the foreground or with the requested size. default_props.set_size(current_props.get_size()) default_props.set_origin(current_props.get_origin()) default_props.set_minimized(False) - default_props.set_foreground(True) + default_props.foreground = current_props.foreground # The rest should be the same assert current_props == default_props From 97eb734765967b21dbd7646627e707beac55b846 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 24 Dec 2017 20:35:23 +0100 Subject: [PATCH 005/118] tests: add test cases for GLSL shaders --- tests/display/conftest.py | 25 +++ tests/display/test_glsl_shader.py | 277 ++++++++++++++++++++++++++++++ 2 files changed, 302 insertions(+) create mode 100644 tests/display/test_glsl_shader.py diff --git a/tests/display/conftest.py b/tests/display/conftest.py index 09684fab84..474ab5ee0b 100644 --- a/tests/display/conftest.py +++ b/tests/display/conftest.py @@ -43,3 +43,28 @@ def window(graphics_pipe, graphics_engine): if win is not None: graphics_engine.remove_window(win) + + +@pytest.fixture(scope='module') +def gsg(graphics_pipe, graphics_engine): + "Returns a windowless GSG that can be used for offscreen rendering." + from panda3d.core import GraphicsPipe, FrameBufferProperties, WindowProperties + + fbprops = FrameBufferProperties() + fbprops.force_hardware = True + + buffer = graphics_engine.make_output( + graphics_pipe, + 'buffer', + 0, + fbprops, + WindowProperties.size(32, 32), + GraphicsPipe.BF_refuse_window + ) + graphics_engine.open_windows() + + assert buffer is not None + yield buffer.gsg + + if buffer is not None: + graphics_engine.remove_window(buffer) diff --git a/tests/display/test_glsl_shader.py b/tests/display/test_glsl_shader.py new file mode 100644 index 0000000000..f4ff2d7ea0 --- /dev/null +++ b/tests/display/test_glsl_shader.py @@ -0,0 +1,277 @@ +from panda3d import core +import pytest +from _pytest.outcomes import Failed + + +# This is the template for the compute shader that is used by run_glsl_test. +# It defines an assert() macro that writes failures to a buffer, indexed by +# line number. +# The reset() function serves to prevent the _triggered variable from being +# optimized out in the case that the assertions are being optimized out. +GLSL_COMPUTE_TEMPLATE = """#version {version} + +layout(local_size_x = 1, local_size_y = 1) in; + +{preamble} + +layout(r8ui) uniform writeonly uimageBuffer _triggered; + +void _reset() {{ + imageStore(_triggered, 0, uvec4(0, 0, 0, 0)); +}} + +void _assert(bool cond, int line) {{ + if (!cond) {{ + imageStore(_triggered, line, uvec4(1)); + }} +}} + +#define assert(cond) _assert(cond, __LINE__) + +void main() {{ + _reset(); +{body} +}} +""" + + +def run_glsl_test(gsg, body, preamble="", inputs={}, version=430): + """ Runs a GLSL test on the given GSG. The given body is executed in the + main function and should call assert(). The preamble should contain all + of the shader inputs. """ + + if not gsg.supports_compute_shaders or not gsg.supports_glsl: + pytest.skip("compute shaders not supported") + + __tracebackhide__ = True + + preamble = preamble.strip() + body = body.rstrip().lstrip('\n') + code = GLSL_COMPUTE_TEMPLATE.format(version=version, preamble=preamble, body=body) + line_offset = code[:code.find(body)].count('\n') + 1 + shader = core.Shader.make_compute(core.Shader.SL_GLSL, code) + assert shader, code + + # Create a buffer to hold the results of the assertion. We use one byte + # per line of shader code, so we can show which lines triggered. + result = core.Texture("") + result.set_clear_color((0, 0, 0, 0)) + result.setup_buffer_texture(code.count('\n'), core.Texture.T_unsigned_byte, + core.Texture.F_r8i, core.GeomEnums.UH_static) + + # Build up the shader inputs + attrib = core.ShaderAttrib.make(shader) + for name, value in inputs.items(): + attrib = attrib.set_shader_input(name, value) + attrib = attrib.set_shader_input('_triggered', result) + + # Run the compute shader. + engine = core.GraphicsEngine.get_global_ptr() + try: + engine.dispatch_compute((1, 1, 1), attrib, gsg) + except AssertionError as exc: + assert False, "Error executing compute shader:\n" + code + + # Download the texture to check whether the assertion triggered. + assert engine.extract_texture_data(result, gsg) + triggered = result.get_ram_image() + if any(triggered): + count = len(triggered) - triggered.count(0) + lines = body.split('\n') + formatted = '' + for i, line in enumerate(lines): + if triggered[i + line_offset]: + formatted += '=> ' + line + '\n' + else: + formatted += ' ' + line + '\n' + pytest.fail("{0} GLSL assertions triggered:\n{1}".format(count, formatted)) + + +def test_glsl_test(gsg): + "Test to make sure that the GLSL tests work correctly." + + run_glsl_test(gsg, "assert(true);") + + +def test_glsl_test_fail(gsg): + "Same as above, but making sure that the failure case works correctly." + + with pytest.raises(Failed): + run_glsl_test(gsg, "assert(false);") + + +def test_glsl_sampler(gsg): + tex1 = core.Texture("") + tex1.setup_1d_texture(1, core.Texture.T_unsigned_byte, core.Texture.F_rgba8) + tex1.set_clear_color((0, 2 / 255.0, 1, 1)) + + tex2 = core.Texture("") + tex2.setup_2d_texture(1, 1, core.Texture.T_float, core.Texture.F_rgba32) + tex2.set_clear_color((1.0, 2.0, -3.14, 0.0)) + + preamble = """ + uniform sampler1D tex1; + uniform sampler2D tex2; + """ + code = """ + assert(texelFetch(tex1, 0, 0) == vec4(0, 2 / 255.0, 1, 1)); + assert(texelFetch(tex2, ivec2(0, 0), 0) == vec4(1.0, 2.0, -3.14, 0.0)); + """ + run_glsl_test(gsg, code, preamble, {'tex1': tex1, 'tex2': tex2}), code + + +def test_glsl_image(gsg): + tex1 = core.Texture("") + tex1.setup_1d_texture(1, core.Texture.T_unsigned_byte, core.Texture.F_rgba8) + tex1.set_clear_color((0, 2 / 255.0, 1, 1)) + + tex2 = core.Texture("") + tex2.setup_2d_texture(1, 1, core.Texture.T_float, core.Texture.F_rgba32) + tex2.set_clear_color((1.0, 2.0, -3.14, 0.0)) + + preamble = """ + layout(rgba8) uniform image1D tex1; + layout(rgba32f) uniform image2D tex2; + """ + code = """ + assert(imageLoad(tex1, 0) == vec4(0, 2 / 255.0, 1, 1)); + assert(imageLoad(tex2, ivec2(0, 0)) == vec4(1.0, 2.0, -3.14, 0.0)); + """ + run_glsl_test(gsg, code, preamble, {'tex1': tex1, 'tex2': tex2}), code + + +def test_glsl_ssbo(gsg): + from struct import pack + num1 = pack(' Date: Wed, 27 Dec 2017 22:56:21 +0100 Subject: [PATCH 006/118] loader: fix crash getting result from async load --- panda/src/event/asyncFuture.I | 5 +++++ panda/src/event/asyncFuture.h | 1 + 2 files changed, 6 insertions(+) diff --git a/panda/src/event/asyncFuture.I b/panda/src/event/asyncFuture.I index 37dca79038..a11fb6b36a 100644 --- a/panda/src/event/asyncFuture.I +++ b/panda/src/event/asyncFuture.I @@ -103,6 +103,11 @@ set_result(TypedReferenceCount *result) { set_result(result, result); } +INLINE void AsyncFuture:: +set_result(TypedWritableReferenceCount *result) { + set_result(result, result); +} + INLINE void AsyncFuture:: set_result(const EventParameter &result) { set_result(result.get_ptr(), result.get_ptr()); diff --git a/panda/src/event/asyncFuture.h b/panda/src/event/asyncFuture.h index 453384fbb5..cbed94b158 100644 --- a/panda/src/event/asyncFuture.h +++ b/panda/src/event/asyncFuture.h @@ -86,6 +86,7 @@ PUBLISHED: INLINE void set_result(nullptr_t); INLINE void set_result(TypedObject *result); INLINE void set_result(TypedReferenceCount *result); + INLINE void set_result(TypedWritableReferenceCount *result); INLINE void set_result(const EventParameter &result); public: From e04ddbef9a49c84823d8a172f75bff283c81864e Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 27 Dec 2017 22:59:38 +0100 Subject: [PATCH 007/118] pipeline: fix multithreaded render pipeline deadlock This deadlock happens when another thread holds a cycler lock and then attempts to call Pipeline::remove_cycler() while Pipeline::cycle() is running on the main thread. The fix for this problem is threefold: * Allow remove_cycler (and add_cycler) to be called while a cycle is in progress, by introducing a second lock * Let cycle() not block if a dirty cycler can't be locked, instead trying other cyclers first * Adding a way to let remove_cycler() check whether a cycler is currently in use by the main thread, and yielding if so More information is on https://github.com/panda3d/panda3d/issues/217 Fixes #217 (also see LP 1186880) --- panda/src/pipeline/pipeline.I | 4 +- panda/src/pipeline/pipeline.cxx | 246 +++++++++++++----- panda/src/pipeline/pipeline.h | 11 +- panda/src/pipeline/pipelineCyclerTrueImpl.I | 4 +- panda/src/pipeline/pipelineCyclerTrueImpl.cxx | 6 +- panda/src/pipeline/pipelineCyclerTrueImpl.h | 5 +- 6 files changed, 205 insertions(+), 71 deletions(-) diff --git a/panda/src/pipeline/pipeline.I b/panda/src/pipeline/pipeline.I index 8eedc7b363..9d3512ae6c 100644 --- a/panda/src/pipeline/pipeline.I +++ b/panda/src/pipeline/pipeline.I @@ -45,7 +45,7 @@ get_num_stages() const { */ INLINE int Pipeline:: get_num_cyclers() const { - ReMutexHolder holder(_lock); + MutexHolder holder(_lock); return _num_cyclers; } #endif // THREADED_PIPELINE @@ -58,7 +58,7 @@ get_num_cyclers() const { */ INLINE int Pipeline:: get_num_dirty_cyclers() const { - ReMutexHolder holder(_lock); + MutexHolder holder(_lock); return _num_dirty_cyclers; } #endif // THREADED_PIPELINE diff --git a/panda/src/pipeline/pipeline.cxx b/panda/src/pipeline/pipeline.cxx index bcd555f003..8d8cd2bebb 100644 --- a/panda/src/pipeline/pipeline.cxx +++ b/panda/src/pipeline/pipeline.cxx @@ -27,7 +27,9 @@ Pipeline(const string &name, int num_stages) : Namable(name), #ifdef THREADED_PIPELINE _num_stages(num_stages), - _lock("Pipeline") + _cycle_lock("Pipeline cycle"), + _lock("Pipeline"), + _next_cycle_seq(1) #else _num_stages(1) #endif @@ -91,87 +93,166 @@ cycle() { } pvector< PT(CycleData) > saved_cdatas; - saved_cdatas.reserve(_num_dirty_cyclers); { - ReMutexHolder holder(_lock); - if (_num_stages == 1) { - // No need to cycle if there's only one stage. - nassertv(_dirty._next == &_dirty); - return; + ReMutexHolder cycle_holder(_cycle_lock); + int prev_seq, next_seq; + PipelineCyclerLinks prev_dirty; + { + // We can't hold the lock protecting the linked lists during the cycling + // itself, since it could cause a deadlock. + MutexHolder holder(_lock); + if (_num_stages == 1) { + // No need to cycle if there's only one stage. + nassertv(_dirty._next == &_dirty); + return; + } + + nassertv(!_cycling); + _cycling = true; + + // Increment the cycle sequence number, which is used by this method to + // communicate with remove_cycler() about the status of dirty cyclers. + prev_seq = next_seq = _next_cycle_seq; + if (++next_seq == 0) { + // Skip 0, which is a reserved number used to indicate a clean cycler. + ++next_seq; + } + _next_cycle_seq = next_seq; + + // Move the dirty list to prev_dirty, for processing. + prev_dirty.make_head(); + prev_dirty.take_list(_dirty); + + saved_cdatas.reserve(_num_dirty_cyclers); + _num_dirty_cyclers = 0; } - nassertv(!_cycling); - _cycling = true; - - // Move the dirty list to prev_dirty, for processing. - PipelineCyclerLinks prev_dirty; - prev_dirty.make_head(); - prev_dirty.take_list(_dirty); - _num_dirty_cyclers = 0; - + // This is duplicated for different number of stages, as an optimization. switch (_num_stages) { case 2: while (prev_dirty._next != &prev_dirty) { - PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)prev_dirty._next; - cycler->remove_from_list(); - ReMutexHolder holder2(cycler->_lock); + PipelineCyclerLinks *link = prev_dirty._next; + while (link != &prev_dirty) { + PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)link; - // We save the result of cycle(), so that we can defer the side- - // effects that might occur when CycleDatas destruct, at least until - // the end of this loop. - saved_cdatas.push_back(cycler->cycle_2()); + if (!cycler->_lock.try_acquire()) { + // No big deal, just move on to the next one for now, and we'll + // come back around to it. It's important not to block here in + // order to prevent one cycler from deadlocking another. + if (link->_prev != &prev_dirty || link->_next != &prev_dirty) { + link = cycler->_next; + continue; + } else { + // Well, we are the last cycler left, so we might as well wait. + // This is necessary to trigger the deadlock detection code. + cycler->_lock.acquire(); + } + } - if (cycler->_dirty) { - // The cycler is still dirty after cycling. Keep it on the dirty - // list for next time. - cycler->insert_before(&_dirty); - ++_num_dirty_cyclers; - } else { - // The cycler is now clean. Add it back to the clean list. + MutexHolder holder(_lock); + cycler->remove_from_list(); + + // We save the result of cycle(), so that we can defer the side- + // effects that might occur when CycleDatas destruct, at least until + // the end of this loop. + saved_cdatas.push_back(cycler->cycle_2()); + + // cycle_2() won't leave a cycler dirty. Add it to the clean list. + nassertd(!cycler->_dirty) break; cycler->insert_before(&_clean); #ifdef DEBUG_THREADS inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); #endif + cycler->_lock.release(); + break; } } break; case 3: while (prev_dirty._next != &prev_dirty) { - PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)prev_dirty._next; - cycler->remove_from_list(); - ReMutexHolder holder2(cycler->_lock); + PipelineCyclerLinks *link = prev_dirty._next; + while (link != &prev_dirty) { + PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)link; - saved_cdatas.push_back(cycler->cycle_3()); + if (!cycler->_lock.try_acquire()) { + // No big deal, just move on to the next one for now, and we'll + // come back around to it. It's important not to block here in + // order to prevent one cycler from deadlocking another. + if (link->_prev != &prev_dirty || link->_next != &prev_dirty) { + link = cycler->_next; + continue; + } else { + // Well, we are the last cycler left, so we might as well wait. + // This is necessary to trigger the deadlock detection code. + cycler->_lock.acquire(); + } + } - if (cycler->_dirty) { - cycler->insert_before(&_dirty); - ++_num_dirty_cyclers; - } else { - cycler->insert_before(&_clean); + MutexHolder holder(_lock); + cycler->remove_from_list(); + + saved_cdatas.push_back(cycler->cycle_3()); + + if (cycler->_dirty) { + // The cycler is still dirty. Add it back to the dirty list. + nassertd(cycler->_dirty == prev_seq) break; + cycler->insert_before(&_dirty); + cycler->_dirty = next_seq; + ++_num_dirty_cyclers; + } else { + // The cycler is now clean. Add it back to the clean list. + cycler->insert_before(&_clean); #ifdef DEBUG_THREADS - inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); + inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); #endif + } + cycler->_lock.release(); + break; } } break; default: while (prev_dirty._next != &prev_dirty) { - PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)prev_dirty._next; - cycler->remove_from_list(); - ReMutexHolder holder2(cycler->_lock); + PipelineCyclerLinks *link = prev_dirty._next; + while (link != &prev_dirty) { + PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)link; - saved_cdatas.push_back(cycler->cycle()); + if (!cycler->_lock.try_acquire()) { + // No big deal, just move on to the next one for now, and we'll + // come back around to it. It's important not to block here in + // order to prevent one cycler from deadlocking another. + if (link->_prev != &prev_dirty || link->_next != &prev_dirty) { + link = cycler->_next; + continue; + } else { + // Well, we are the last cycler left, so we might as well wait. + // This is necessary to trigger the deadlock detection code. + cycler->_lock.acquire(); + } + } - if (cycler->_dirty) { - cycler->insert_before(&_dirty); - ++_num_dirty_cyclers; - } else { - cycler->insert_before(&_clean); + MutexHolder holder(_lock); + cycler->remove_from_list(); + + saved_cdatas.push_back(cycler->cycle()); + + if (cycler->_dirty) { + // The cycler is still dirty. Add it back to the dirty list. + nassertd(cycler->_dirty == prev_seq) break; + cycler->insert_before(&_dirty); + cycler->_dirty = next_seq; + ++_num_dirty_cyclers; + } else { + // The cycler is now clean. Add it back to the clean list. + cycler->insert_before(&_clean); #ifdef DEBUG_THREADS - inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); + inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); #endif + } + cycler->_lock.release(); + break; } } break; @@ -203,7 +284,9 @@ void Pipeline:: set_num_stages(int num_stages) { nassertv(num_stages >= 1); #ifdef THREADED_PIPELINE - ReMutexHolder holder(_lock); + // Make sure it's not currently cycling. + ReMutexHolder cycle_holder(_cycle_lock); + MutexHolder holder(_lock); if (num_stages != _num_stages) { // We need to lock every PipelineCycler object attached to this pipeline @@ -261,9 +344,10 @@ set_num_stages(int num_stages) { */ void Pipeline:: add_cycler(PipelineCyclerTrueImpl *cycler) { - ReMutexHolder holder(_lock); + // It's safe to add it to the list while cycling, since the _clean list is + // not touched during the cycle loop. + MutexHolder holder(_lock); nassertv(!cycler->_dirty); - nassertv(!_cycling); cycler->insert_before(&_clean); ++_num_cyclers; @@ -285,15 +369,16 @@ void Pipeline:: add_dirty_cycler(PipelineCyclerTrueImpl *cycler) { nassertv(cycler->_lock.debug_is_locked()); - ReMutexHolder holder(_lock); - nassertv(_num_stages != 1); - nassertv(!_cycling); + // It's safe to add it to the list while cycling, since it's not currently + // on the dirty list. + MutexHolder holder(_lock); nassertv(!cycler->_dirty); + nassertv(_num_stages != 1); // Remove it from the "clean" list and add it to the "dirty" list. cycler->remove_from_list(); cycler->insert_before(&_dirty); - cycler->_dirty = true; + cycler->_dirty = _next_cycle_seq; ++_num_dirty_cyclers; #ifdef DEBUG_THREADS @@ -311,9 +396,42 @@ void Pipeline:: remove_cycler(PipelineCyclerTrueImpl *cycler) { nassertv(cycler->_lock.debug_is_locked()); - ReMutexHolder holder(_lock); - nassertv(!_cycling); + MutexHolder holder(_lock); + // If it's dirty, it may currently be processed by cycle(), so we need to be + // careful not to cause a race condition. It's safe for us to remove it + // during cycle only if it's 0 (clean) or _next_cycle_seq (scheduled for the + // next cycle, so not owned by the current one). + while (cycler->_dirty != 0 && cycler->_dirty != _next_cycle_seq) { + if (_cycle_lock.try_acquire()) { + // OK, great, we got the lock, so it finished cycling already. + nassertv(!_cycling); + + --_num_cyclers; + cycler->remove_from_list(); + + cycler->_dirty = false; + --_num_dirty_cyclers; + + #ifdef DEBUG_THREADS + inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), -1); + inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); + #endif + + _cycle_lock.release(); + return; + } else { + // It's possibly currently being cycled. We will wait for the cycler + // to be done with it, so that we can safely remove it. + _lock.release(); + cycler->_lock.release(); + Thread::force_yield(); + cycler->_lock.acquire(); + _lock.acquire(); + } + } + + // It's not being owned by a cycle operation, so it's fair game. --_num_cyclers; cycler->remove_from_list(); @@ -322,7 +440,7 @@ remove_cycler(PipelineCyclerTrueImpl *cycler) { #endif if (cycler->_dirty) { - cycler->_dirty = false; + cycler->_dirty = 0; --_num_dirty_cyclers; #ifdef DEBUG_THREADS inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); @@ -341,7 +459,9 @@ remove_cycler(PipelineCyclerTrueImpl *cycler) { */ void Pipeline:: iterate_all_cycler_types(CallbackFunc *func, void *data) const { - ReMutexHolder holder(_lock); + // Make sure it's not currently cycling. + ReMutexHolder cycle_holder(_cycle_lock); + MutexHolder holder(_lock); TypeCount::const_iterator ci; for (ci = _all_cycler_types.begin(); ci != _all_cycler_types.end(); ++ci) { func((*ci).first, (*ci).second, data); @@ -356,7 +476,9 @@ iterate_all_cycler_types(CallbackFunc *func, void *data) const { */ void Pipeline:: iterate_dirty_cycler_types(CallbackFunc *func, void *data) const { - ReMutexHolder holder(_lock); + // Make sure it's not currently cycling. + ReMutexHolder cycle_holder(_cycle_lock); + MutexHolder holder(_lock); TypeCount::const_iterator ci; for (ci = _dirty_cycler_types.begin(); ci != _dirty_cycler_types.end(); ++ci) { func((*ci).first, (*ci).second, data); diff --git a/panda/src/pipeline/pipeline.h b/panda/src/pipeline/pipeline.h index 32e9cafad7..3311496ff8 100644 --- a/panda/src/pipeline/pipeline.h +++ b/panda/src/pipeline/pipeline.h @@ -85,7 +85,16 @@ private: // This is true only during cycle(). bool _cycling; - ReMutex _lock; + // This increases with every cycle run. If the _dirty field of a cycler is + // set to the same value as this, it indicates that it is scheduled for the + // next cycle. + unsigned int _next_cycle_seq; + + // This lock is always held during cycle(). + ReMutex _cycle_lock; + + // This lock protects the data stored on this Pipeline. + Mutex _lock; #endif // THREADED_PIPELINE }; diff --git a/panda/src/pipeline/pipelineCyclerTrueImpl.I b/panda/src/pipeline/pipelineCyclerTrueImpl.I index 9b078a6a30..fa6a97499b 100644 --- a/panda/src/pipeline/pipelineCyclerTrueImpl.I +++ b/panda/src/pipeline/pipelineCyclerTrueImpl.I @@ -393,7 +393,7 @@ cycle_2() { _data[1]._cdata = _data[0]._cdata; // No longer dirty. - _dirty = false; + _dirty = 0; return last_val; } @@ -423,7 +423,7 @@ cycle_3() { if (_data[2]._cdata == _data[1]._cdata) { // No longer dirty. - _dirty = false; + _dirty = 0; } return last_val; diff --git a/panda/src/pipeline/pipelineCyclerTrueImpl.cxx b/panda/src/pipeline/pipelineCyclerTrueImpl.cxx index b1466ce155..f943a030f8 100644 --- a/panda/src/pipeline/pipelineCyclerTrueImpl.cxx +++ b/panda/src/pipeline/pipelineCyclerTrueImpl.cxx @@ -24,7 +24,7 @@ PipelineCyclerTrueImpl:: PipelineCyclerTrueImpl(CycleData *initial_data, Pipeline *pipeline) : _pipeline(pipeline), - _dirty(false), + _dirty(0), _lock(this) { if (_pipeline == (Pipeline *)NULL) { @@ -46,7 +46,7 @@ PipelineCyclerTrueImpl(CycleData *initial_data, Pipeline *pipeline) : PipelineCyclerTrueImpl:: PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl ©) : _pipeline(copy._pipeline), - _dirty(false), + _dirty(0), _lock(this) { ReMutexHolder holder(_lock); @@ -278,7 +278,7 @@ cycle() { } // No longer dirty. - _dirty = false; + _dirty = 0; return last_val; } diff --git a/panda/src/pipeline/pipelineCyclerTrueImpl.h b/panda/src/pipeline/pipelineCyclerTrueImpl.h index 3aa7ffc232..59896c8b18 100644 --- a/panda/src/pipeline/pipelineCyclerTrueImpl.h +++ b/panda/src/pipeline/pipelineCyclerTrueImpl.h @@ -122,7 +122,10 @@ private: }; CycleDataNode *_data; int _num_stages; - bool _dirty; + + // This is 0 if it's clean, or set to Pipeline::_next_cycle_seq if it's + // scheduled to be cycled during the next cycle() call. + unsigned int _dirty; CyclerMutex _lock; From 39abc6602512631f29f9d6201e298330618e6520 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 27 Dec 2017 23:51:16 +0100 Subject: [PATCH 008/118] gobj: reduce unnecessary use of threading primitives in constructors --- panda/src/gobj/geom.I | 21 ++++++---------- panda/src/gobj/geom.cxx | 34 ++++++++++---------------- panda/src/gobj/geom.h | 5 ++-- panda/src/gobj/geomVertexArrayData.I | 16 ++++++++++-- panda/src/gobj/geomVertexArrayData.cxx | 17 ++++--------- panda/src/gobj/geomVertexArrayData.h | 3 ++- panda/src/gobj/geomVertexData.I | 17 ++++++------- panda/src/gobj/geomVertexData.cxx | 18 ++------------ panda/src/gobj/geomVertexData.h | 3 ++- panda/src/pipeline/cycleData.I | 7 ------ panda/src/pipeline/cycleData.h | 4 ++- panda/src/pipeline/pipelineCycler.I | 21 ++++++++++++++++ panda/src/pipeline/pipelineCycler.h | 4 ++- 13 files changed, 83 insertions(+), 87 deletions(-) diff --git a/panda/src/gobj/geom.I b/panda/src/gobj/geom.I index d553c12977..340cfd099c 100644 --- a/panda/src/gobj/geom.I +++ b/panda/src/gobj/geom.I @@ -512,22 +512,17 @@ CData() : * */ INLINE Geom::CData:: -CData(const Geom::CData ©) : - _data(copy._data), - _primitives(copy._primitives), - _primitive_type(copy._primitive_type), - _shade_model(copy._shade_model), - _geom_rendering(copy._geom_rendering), - _modified(copy._modified), - _internal_bounds(copy._internal_bounds), - _nested_vertices(copy._nested_vertices), - _internal_bounds_stale(copy._internal_bounds_stale), - _bounds_type(copy._bounds_type), - _user_bounds(copy._user_bounds) +CData(GeomVertexData *data) : + _data(data), + _primitive_type(PT_none), + _shade_model(SM_uniform), + _geom_rendering(0), + _nested_vertices(0), + _internal_bounds_stale(true), + _bounds_type(BoundingVolume::BT_default) { } - /** * */ diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx index 8590342010..efdd4ba5ca 100644 --- a/panda/src/gobj/geom.cxx +++ b/panda/src/gobj/geom.cxx @@ -46,13 +46,7 @@ make_cow_copy() { * */ Geom:: -Geom(const GeomVertexData *data) { - // Let's ensure the vertex data gets set on all stages at once. - OPEN_ITERATE_ALL_STAGES(_cycler) { - CDStageWriter cdata(_cycler, pipeline_stage); - cdata->_data = (GeomVertexData *)data; - } - CLOSE_ITERATE_ALL_STAGES(_cycler); +Geom(const GeomVertexData *data) : _cycler(CData((GeomVertexData *)data)) { } /** @@ -631,7 +625,7 @@ unify_in_place(int max_indices, bool preserve_order) { } else { // We have already encountered another primitive of this type. Combine // them. - combine_primitives((*npi).second, primitive, current_thread); + combine_primitives((*npi).second, move(primitive), current_thread); } } @@ -1491,31 +1485,29 @@ reset_geom_rendering(Geom::CData *cdata) { * is modified to append the vertices from b_prim, which is unmodified. */ void Geom:: -combine_primitives(GeomPrimitive *a_prim, const GeomPrimitive *b_prim, +combine_primitives(GeomPrimitive *a_prim, CPT(GeomPrimitive) b_prim, Thread *current_thread) { nassertv(a_prim != b_prim); nassertv(a_prim->get_type() == b_prim->get_type()); - CPT(GeomPrimitive) b_prim2 = b_prim; - - if (a_prim->get_index_type() != b_prim2->get_index_type()) { - GeomPrimitive::NumericType index_type = max(a_prim->get_index_type(), b_prim2->get_index_type()); + if (a_prim->get_index_type() != b_prim->get_index_type()) { + GeomPrimitive::NumericType index_type = max(a_prim->get_index_type(), b_prim->get_index_type()); a_prim->set_index_type(index_type); - if (b_prim2->get_index_type() != index_type) { - PT(GeomPrimitive) b_prim_copy = b_prim2->make_copy(); + if (b_prim->get_index_type() != index_type) { + PT(GeomPrimitive) b_prim_copy = b_prim->make_copy(); b_prim_copy->set_index_type(index_type); - b_prim2 = b_prim_copy; + b_prim = b_prim_copy; } } - if (!b_prim2->is_indexed()) { - PT(GeomPrimitive) b_prim_copy = b_prim2->make_copy(); + if (!b_prim->is_indexed()) { + PT(GeomPrimitive) b_prim_copy = b_prim->make_copy(); b_prim_copy->make_indexed(); - b_prim2 = b_prim_copy; + b_prim = b_prim_copy; } PT(GeomVertexArrayData) a_vertices = a_prim->modify_vertices(); - CPT(GeomVertexArrayData) b_vertices = b_prim2->get_vertices(); + CPT(GeomVertexArrayData) b_vertices = b_prim->get_vertices(); if (a_prim->requires_unused_vertices()) { GeomVertexReader index(b_vertices, 0); @@ -1536,7 +1528,7 @@ combine_primitives(GeomPrimitive *a_prim, const GeomPrimitive *b_prim, if (a_prim->is_composite()) { // Also copy the ends array. PTA_int a_ends = a_prim->modify_ends(); - CPTA_int b_ends = b_prim2->get_ends(); + CPTA_int b_ends = b_prim->get_ends(); for (size_t i = 0; i < b_ends.size(); ++i) { a_ends.push_back(b_ends[i] + orig_a_vertices); } diff --git a/panda/src/gobj/geom.h b/panda/src/gobj/geom.h index 8b220a5a4a..ec81442703 100644 --- a/panda/src/gobj/geom.h +++ b/panda/src/gobj/geom.h @@ -196,7 +196,7 @@ private: void reset_geom_rendering(CData *cdata); - void combine_primitives(GeomPrimitive *a_prim, const GeomPrimitive *b_prim, + void combine_primitives(GeomPrimitive *a_prim, CPT(GeomPrimitive) b_prim, Thread *current_thread); private: @@ -302,7 +302,8 @@ private: class EXPCL_PANDA_GOBJ CData : public CycleData { public: INLINE CData(); - INLINE CData(const CData ©); + INLINE CData(GeomVertexData *data); + ALLOC_DELETED_CHAIN(CData); virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; diff --git a/panda/src/gobj/geomVertexArrayData.I b/panda/src/gobj/geomVertexArrayData.I index 351f56cd53..624525360d 100644 --- a/panda/src/gobj/geomVertexArrayData.I +++ b/panda/src/gobj/geomVertexArrayData.I @@ -229,8 +229,20 @@ mark_used() { * */ INLINE GeomVertexArrayData::CData:: -CData() : - _usage_hint(UH_unspecified), +CData(UsageHint usage_hint) : + _usage_hint(usage_hint), + _rw_lock("GeomVertexArrayData::CData::_rw_lock") +{ +} + +/** + * + */ +INLINE GeomVertexArrayData::CData:: +CData(GeomVertexArrayData::CData &&from) NOEXCEPT : + _usage_hint(move(from._usage_hint)), + _buffer(move(from._buffer)), + _modified(move(from._modified)), _rw_lock("GeomVertexArrayData::CData::_rw_lock") { } diff --git a/panda/src/gobj/geomVertexArrayData.cxx b/panda/src/gobj/geomVertexArrayData.cxx index 2747e22395..50a4529159 100644 --- a/panda/src/gobj/geomVertexArrayData.cxx +++ b/panda/src/gobj/geomVertexArrayData.cxx @@ -77,16 +77,10 @@ GeomVertexArrayData:: GeomVertexArrayData(const GeomVertexArrayFormat *array_format, GeomVertexArrayData::UsageHint usage_hint) : SimpleLruPage(0), - _array_format(array_format) + _array_format(array_format), + _cycler(CData(usage_hint)), + _contexts(nullptr) { - OPEN_ITERATE_ALL_STAGES(_cycler) { - CDStageWriter cdata(_cycler, pipeline_stage); - cdata->_usage_hint = usage_hint; - } - CLOSE_ITERATE_ALL_STAGES(_cycler); - - _contexts = NULL; - set_lru_size(0); nassertv(_array_format->is_registered()); } @@ -99,10 +93,9 @@ GeomVertexArrayData(const GeomVertexArrayData ©) : CopyOnWriteObject(copy), SimpleLruPage(copy), _array_format(copy._array_format), - _cycler(copy._cycler) + _cycler(copy._cycler), + _contexts(nullptr) { - _contexts = NULL; - copy.mark_used_lru(); set_lru_size(get_data_size_bytes()); diff --git a/panda/src/gobj/geomVertexArrayData.h b/panda/src/gobj/geomVertexArrayData.h index c16c5831d9..4a49d26881 100644 --- a/panda/src/gobj/geomVertexArrayData.h +++ b/panda/src/gobj/geomVertexArrayData.h @@ -150,7 +150,8 @@ private: // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA_GOBJ CData : public CycleData { public: - INLINE CData(); + INLINE CData(UsageHint usage_hint = UH_unspecified); + INLINE CData(CData &&from) NOEXCEPT; INLINE CData(const CData ©); INLINE void operator = (const CData ©); diff --git a/panda/src/gobj/geomVertexData.I b/panda/src/gobj/geomVertexData.I index 53c1c76367..5a24b166a5 100644 --- a/panda/src/gobj/geomVertexData.I +++ b/panda/src/gobj/geomVertexData.I @@ -583,17 +583,14 @@ CData() : * */ INLINE GeomVertexData::CData:: -CData(const GeomVertexData::CData ©) : - _usage_hint(copy._usage_hint), - _format(copy._format), - _arrays(copy._arrays), - _transform_table(copy._transform_table), - _transform_blend_table(copy._transform_blend_table), - _slider_table(copy._slider_table), - _animated_vertices(copy._animated_vertices), - _animated_vertices_modified(copy._animated_vertices_modified), - _modified(copy._modified) +CData(const GeomVertexFormat *format, GeomVertexData::UsageHint usage_hint) : + _format(format), + _usage_hint(usage_hint) { + size_t num_arrays = format->get_num_arrays(); + for (size_t i = 0; i < num_arrays; ++i) { + _arrays.push_back(new GeomVertexArrayData(format->get_array(i), usage_hint)); + } } /** diff --git a/panda/src/gobj/geomVertexData.cxx b/panda/src/gobj/geomVertexData.cxx index 339f38114e..b692c36bf5 100644 --- a/panda/src/gobj/geomVertexData.cxx +++ b/panda/src/gobj/geomVertexData.cxx @@ -67,24 +67,10 @@ GeomVertexData(const string &name, _char_pcollector(PStatCollector(_animation_pcollector, name)), _skinning_pcollector(_char_pcollector, "Skinning"), _morphs_pcollector(_char_pcollector, "Morphs"), - _blends_pcollector(_char_pcollector, "Calc blends") + _blends_pcollector(_char_pcollector, "Calc blends"), + _cycler(GeomVertexData::CData(format, usage_hint)) { nassertv(format->is_registered()); - - // Create some empty arrays as required by the format. Let's ensure the - // vertex data gets set on all stages at once. - OPEN_ITERATE_ALL_STAGES(_cycler) { - CDStageWriter cdata(_cycler, pipeline_stage); - cdata->_format = format; - cdata->_usage_hint = usage_hint; - int num_arrays = format->get_num_arrays(); - for (int i = 0; i < num_arrays; i++) { - PT(GeomVertexArrayData) array = new GeomVertexArrayData - (format->get_array(i), usage_hint); - cdata->_arrays.push_back(array.p()); - } - } - CLOSE_ITERATE_ALL_STAGES(_cycler); } /** diff --git a/panda/src/gobj/geomVertexData.h b/panda/src/gobj/geomVertexData.h index f2858d5a16..d86dd36fc4 100644 --- a/panda/src/gobj/geomVertexData.h +++ b/panda/src/gobj/geomVertexData.h @@ -293,7 +293,8 @@ private: class EXPCL_PANDA_GOBJ CData : public CycleData { public: INLINE CData(); - INLINE CData(const CData ©); + INLINE CData(const GeomVertexFormat *format, UsageHint usage_hint); + ALLOC_DELETED_CHAIN(CData); virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; diff --git a/panda/src/pipeline/cycleData.I b/panda/src/pipeline/cycleData.I index 53c0224eab..b66184d145 100644 --- a/panda/src/pipeline/cycleData.I +++ b/panda/src/pipeline/cycleData.I @@ -10,10 +10,3 @@ * @author drose * @date 2002-02-21 */ - -/** - * - */ -INLINE CycleData:: -CycleData() { -} diff --git a/panda/src/pipeline/cycleData.h b/panda/src/pipeline/cycleData.h index 4a020c9940..72ba074088 100644 --- a/panda/src/pipeline/cycleData.h +++ b/panda/src/pipeline/cycleData.h @@ -49,7 +49,9 @@ class EXPCL_PANDA_PIPELINE CycleData #endif // DO_PIPELINING { public: - INLINE CycleData(); + INLINE CycleData() DEFAULT_CTOR; + INLINE CycleData(CycleData &&from) DEFAULT_CTOR; + INLINE CycleData(const CycleData ©) DEFAULT_CTOR; virtual ~CycleData(); virtual CycleData *make_copy() const=0; diff --git a/panda/src/pipeline/pipelineCycler.I b/panda/src/pipeline/pipelineCycler.I index 8efce5450a..06c6c1e5dc 100644 --- a/panda/src/pipeline/pipelineCycler.I +++ b/panda/src/pipeline/pipelineCycler.I @@ -25,6 +25,16 @@ PipelineCycler(Pipeline *pipeline) : { } +/** + * + */ +template +INLINE PipelineCycler:: +PipelineCycler(CycleDataType &&initial_data, Pipeline *pipeline) : + PipelineCyclerBase(new CycleDataType(move(initial_data)), pipeline) +{ +} + /** * */ @@ -182,6 +192,17 @@ PipelineCycler(Pipeline *pipeline) : { } +/** + * + */ +template +INLINE PipelineCycler:: +PipelineCycler(CycleDataType &&initial_data, Pipeline *pipeline) : + _typed_data(move(initial_data)), + PipelineCyclerBase(&_typed_data, pipeline) +{ +} + /** * */ diff --git a/panda/src/pipeline/pipelineCycler.h b/panda/src/pipeline/pipelineCycler.h index f87475c192..adc7abd1a8 100644 --- a/panda/src/pipeline/pipelineCycler.h +++ b/panda/src/pipeline/pipelineCycler.h @@ -45,7 +45,9 @@ template struct PipelineCycler : public PipelineCyclerBase { public: - INLINE PipelineCycler(Pipeline *pipeline = NULL); + INLINE PipelineCycler(Pipeline *pipeline = nullptr); + INLINE PipelineCycler(CycleDataType &&initial_data, Pipeline *pipeline = nullptr); + INLINE PipelineCycler(const PipelineCycler ©); INLINE void operator = (const PipelineCycler ©); From 7ee9467f8da4211b82e9f40dd194f8204271cf78 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 30 Dec 2017 17:44:52 +0100 Subject: [PATCH 009/118] shadows: fix shadow buffer creation deadlock in multithreaded pipeline Fixes #162 --- panda/src/display/displayRegion.cxx | 70 ++++--- panda/src/display/graphicsEngine.cxx | 198 ++++++++++-------- panda/src/display/graphicsEngine.h | 11 +- panda/src/display/graphicsStateGuardian.cxx | 150 +++++-------- panda/src/display/graphicsStateGuardian.h | 2 +- panda/src/display/windowProperties.cxx | 6 + panda/src/display/windowProperties.h | 1 + .../glstuff/glGraphicsStateGuardian_src.cxx | 30 +++ .../src/glstuff/glGraphicsStateGuardian_src.h | 2 + panda/src/pgraphnodes/lightLensNode.I | 17 +- panda/src/pgraphnodes/lightLensNode.cxx | 40 +++- panda/src/pgraphnodes/lightLensNode.h | 6 +- panda/src/pgraphnodes/pointLight.cxx | 30 +++ panda/src/pgraphnodes/pointLight.h | 2 + 14 files changed, 335 insertions(+), 230 deletions(-) diff --git a/panda/src/display/displayRegion.cxx b/panda/src/display/displayRegion.cxx index 4ffa29285c..99aea51477 100644 --- a/panda/src/display/displayRegion.cxx +++ b/panda/src/display/displayRegion.cxx @@ -84,22 +84,30 @@ DisplayRegion:: */ void DisplayRegion:: cleanup() { - set_camera(NodePath()); + CDStageWriter cdata(_cycler, 0); + if (cdata->_camera_node != nullptr) { + // We need to tell the old camera we're not using it anymore. + cdata->_camera_node->remove_display_region(this); + } + cdata->_camera_node = nullptr; + cdata->_camera = NodePath(); - CDCullWriter cdata(_cycler_cull, true); - cdata->_cull_result = NULL; + CDCullWriter cdata_cull(_cycler_cull, true); + cdata_cull->_cull_result = nullptr; } /** * Sets the lens index, allows for multiple lenses to be attached to a camera. * This is useful for a variety of setups, such as fish eye rendering. The * default is 0. + * + * Don't call this in a downstream thread unless you don't mind it blowing + * away other changes you might have recently made in an upstream thread. */ void DisplayRegion:: set_lens_index(int index) { - int pipeline_stage = Thread::get_current_pipeline_stage(); - nassertv(pipeline_stage == 0); - CDWriter cdata(_cycler); + Thread *current_thread = Thread::get_current_thread(); + CDWriter cdata(_cycler, true, current_thread); cdata->_lens_index = index; } @@ -107,12 +115,14 @@ set_lens_index(int index) { * Changes the portion of the framebuffer this DisplayRegion corresponds to. * The parameters range from 0 to 1, where 0,0 is the lower left corner and * 1,1 is the upper right; (0, 1, 0, 1) represents the whole screen. + * + * Don't call this in a downstream thread unless you don't mind it blowing + * away other changes you might have recently made in an upstream thread. */ void DisplayRegion:: set_dimensions(int i, const LVecBase4 &dimensions) { - int pipeline_stage = Thread::get_current_pipeline_stage(); - nassertv(pipeline_stage == 0); - CDWriter cdata(_cycler); + Thread *current_thread = Thread::get_current_thread(); + CDWriter cdata(_cycler, true, current_thread); cdata->_regions[i]._dimensions = dimensions; @@ -145,15 +155,13 @@ is_stereo() const { * * The camera is actually set via a NodePath, which clarifies which instance * of the camera (if there happen to be multiple instances) we should use. + * + * Don't call this in a downstream thread unless you don't mind it blowing + * away other changes you might have recently made in an upstream thread. */ void DisplayRegion:: set_camera(const NodePath &camera) { - int pipeline_stage = Thread::get_current_pipeline_stage(); - - // We allow set_camera(NodePath()) to happen in cleanup(), which can be - // called from any pipeline stage. - nassertv(pipeline_stage == 0 || camera.is_empty()); - CDStageWriter cdata(_cycler, 0); + CDWriter cdata(_cycler, true); Camera *camera_node = (Camera *)NULL; if (!camera.is_empty()) { @@ -181,16 +189,17 @@ set_camera(const NodePath &camera) { /** * Sets the active flag associated with the DisplayRegion. If the * DisplayRegion is marked inactive, nothing is rendered. + * + * Don't call this in a downstream thread unless you don't mind it blowing + * away other changes you might have recently made in an upstream thread. */ void DisplayRegion:: set_active(bool active) { - int pipeline_stage = Thread::get_current_pipeline_stage(); - nassertv(pipeline_stage == 0); - CDLockedReader cdata(_cycler); + Thread *current_thread = Thread::get_current_thread(); + CDWriter cdata(_cycler, true, current_thread); if (active != cdata->_active) { - CDWriter cdataw(_cycler, cdata); - cdataw->_active = active; + cdata->_active = active; win_display_regions_changed(); } } @@ -199,15 +208,17 @@ set_active(bool active) { * Sets the sort value associated with the DisplayRegion. Within a window, * DisplayRegions will be rendered in order from the lowest sort value to the * highest. + * + * Don't call this in a downstream thread unless you don't mind it blowing + * away other changes you might have recently made in an upstream thread. */ void DisplayRegion:: set_sort(int sort) { - nassertv(Thread::get_current_pipeline_stage() == 0); - CDLockedReader cdata(_cycler); + Thread *current_thread = Thread::get_current_thread(); + CDWriter cdata(_cycler, true, current_thread); if (sort != cdata->_sort) { - CDWriter cdataw(_cycler, cdata); - cdataw->_sort = sort; + cdata->_sort = sort; win_display_regions_changed(); } } @@ -332,12 +343,14 @@ get_cull_traverser() { * * This is particularly useful when rendering cube maps and/or stereo * textures. + * + * Don't call this in a downstream thread unless you don't mind it blowing + * away other changes you might have recently made in an upstream thread. */ void DisplayRegion:: set_target_tex_page(int page) { - int pipeline_stage = Thread::get_current_pipeline_stage(); - nassertv(pipeline_stage == 0); - CDWriter cdata(_cycler); + Thread *current_thread = Thread::get_current_thread(); + CDWriter cdata(_cycler, true, current_thread); cdata->_target_tex_page = page; } @@ -555,9 +568,6 @@ compute_pixels() { */ void DisplayRegion:: compute_pixels_all_stages() { - int pipeline_stage = Thread::get_current_pipeline_stage(); - nassertv(pipeline_stage == 0); - if (_window != (GraphicsOutput *)NULL) { OPEN_ITERATE_ALL_STAGES(_cycler) { CDStageWriter cdata(_cycler, pipeline_stage); diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index 9c11fc9bdc..1a38089e01 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -153,7 +153,6 @@ GraphicsEngine(Pipeline *pipeline) : _windows_sorted = true; _window_sort_index = 0; - _needs_open_windows = false; set_threading_model(GraphicsThreadingModel(threading_model)); if (!_threading_model.is_default()) { @@ -326,13 +325,10 @@ make_output(GraphicsPipe *pipe, // Sanity check everything. - GraphicsThreadingModel threading_model = get_threading_model(); nassertr(pipe != (GraphicsPipe *)NULL, NULL); if (gsg != (GraphicsStateGuardian *)NULL) { nassertr(pipe == gsg->get_pipe(), NULL); nassertr(this == gsg->get_engine(), NULL); - nassertr(threading_model.get_draw_name() == - gsg->get_threading_model().get_draw_name(), NULL); } // Are we really asking for a callback window? @@ -346,8 +342,8 @@ make_output(GraphicsPipe *pipe, if (this_gsg != (GraphicsStateGuardian *)NULL) { CallbackGraphicsWindow *window = new CallbackGraphicsWindow(this, pipe, name, fb_prop, win_prop, flags, this_gsg); window->_sort = sort; - do_add_window(window, threading_model); - do_add_gsg(window->get_gsg(), pipe, threading_model); + do_add_window(window); + do_add_gsg(window->get_gsg(), pipe); display_cat.info() << "Created output of type CallbackGraphicsWindow\n"; return window; } @@ -386,8 +382,8 @@ make_output(GraphicsPipe *pipe, (host->get_fb_properties().subsumes(fb_prop))) { ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags); buffer->_sort = sort; - do_add_window(buffer, threading_model); - do_add_gsg(host->get_gsg(), pipe, threading_model); + do_add_window(buffer); + do_add_gsg(host->get_gsg(), pipe); display_cat.info() << "Created output of type ParasiteBuffer\n"; return buffer; } @@ -398,8 +394,8 @@ make_output(GraphicsPipe *pipe, if (force_parasite_buffer && can_use_parasite) { ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags); buffer->_sort = sort; - do_add_window(buffer, threading_model); - do_add_gsg(host->get_gsg(), pipe, threading_model); + do_add_window(buffer); + do_add_gsg(host->get_gsg(), pipe); display_cat.info() << "Created output of type ParasiteBuffer\n"; return buffer; } @@ -412,17 +408,15 @@ make_output(GraphicsPipe *pipe, pipe->make_output(name, fb_prop, win_prop, flags, this, gsg, host, retry, precertify); if (window != (GraphicsOutput *)NULL) { window->_sort = sort; - if ((precertify) && (gsg != 0) && (window->get_gsg()==gsg)) { - do_add_window(window, threading_model); - do_add_gsg(window->get_gsg(), pipe, threading_model); + if (precertify && gsg != nullptr && window->get_gsg() == gsg) { + do_add_window(window); display_cat.info() << "Created output of type " << window->get_type() << "\n"; return window; } - do_add_window(window, threading_model); + do_add_window(window); open_windows(); if (window->is_valid()) { - do_add_gsg(window->get_gsg(), pipe, threading_model); display_cat.info() << "Created output of type " << window->get_type() << "\n"; @@ -462,8 +456,8 @@ make_output(GraphicsPipe *pipe, if (can_use_parasite) { ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags); buffer->_sort = sort; - do_add_window(buffer, threading_model); - do_add_gsg(host->get_gsg(), pipe, threading_model); + do_add_window(buffer); + do_add_gsg(host->get_gsg(), pipe); display_cat.info() << "Created output of type ParasiteBuffer\n"; return buffer; } @@ -479,30 +473,24 @@ make_output(GraphicsPipe *pipe, * shouldn't be called by user code as make_output normally does this under * the hood; it may be useful in esoteric cases in which a custom window * object is used. + * + * This can be called during the rendering loop, unlike make_output(); the + * window will be opened before the next frame begins rendering. Because it + * doesn't call open_windows(), however, it's not guaranteed that the window + * will succeed opening even if it returns true. */ bool GraphicsEngine:: add_window(GraphicsOutput *window, int sort) { - nassertr(window != NULL, false); - - GraphicsThreadingModel threading_model = get_threading_model(); + nassertr(window != nullptr, false); nassertr(this == window->get_engine(), false); window->_sort = sort; - do_add_window(window, threading_model); + do_add_window(window); - open_windows(); - if (window->is_valid()) { - do_add_gsg(window->get_gsg(), window->get_pipe(), threading_model); + display_cat.info() + << "Added output of type " << window->get_type() << "\n"; - display_cat.info() - << "Added output of type " << window->get_type() << "\n"; - - return true; - - } else { - remove_window(window); - return false; - } + return true; } /** @@ -537,6 +525,13 @@ remove_window(GraphicsOutput *window) { } count = _windows.erase(ptwin); } + + // Also check whether it is in _new_windows. + { + MutexHolder new_windows_holder(_new_windows_lock, current_thread); + _new_windows.erase(std::remove(_new_windows.begin(), _new_windows.end(), ptwin)); + } + if (count == 0) { // Never heard of this window. Do nothing. return false; @@ -601,6 +596,11 @@ remove_all_windows() { } } + { + MutexHolder new_windows_holder(_new_windows_lock, current_thread); + _new_windows.clear(); + } + _app.do_close(this, current_thread); _app.do_pending(this, current_thread); terminate_threads(current_thread); @@ -694,14 +694,12 @@ render_frame() { } #endif - if (_needs_open_windows) { - // Make sure our buffers and windows are fully realized before we render a - // frame. We do this particularly to realize our offscreen buffers, so - // that we don't render a frame before the offscreen buffers are ready - // (which might result in a frame going by without some textures having - // been rendered). - open_windows(); - } + // Make sure our buffers and windows are fully realized before we render a + // frame. We do this particularly to realize our offscreen buffers, so + // that we don't render a frame before the offscreen buffers are ready + // (which might result in a frame going by without some textures having + // been rendered). + open_windows(); ClockObject *global_clock = ClockObject::get_global_clock(); @@ -945,10 +943,58 @@ open_windows() { ReMutexHolder holder(_lock, current_thread); - if (!_windows_sorted) { - do_resort_windows(); + pvector new_windows; + { + MutexHolder new_windows_holder(_new_windows_lock, current_thread); + if (_new_windows.empty()) { + return; + } + + for (auto it = _new_windows.begin(); it != _new_windows.end(); ++it) { + GraphicsOutput *window = *it; + + WindowRenderer *cull = + get_window_renderer(_threading_model.get_cull_name(), + _threading_model.get_cull_stage()); + WindowRenderer *draw = + get_window_renderer(_threading_model.get_draw_name(), + _threading_model.get_draw_stage()); + + if (_threading_model.get_cull_sorting()) { + cull->add_window(cull->_cull, window); + draw->add_window(draw->_draw, window); + } else { + cull->add_window(cull->_cdraw, window); + } + + // Ask the pipe which thread it prefers to run its windowing commands in + // (the "window thread"). This is the thread that handles the commands + // to open, resize, etc. the window. X requires this to be done in the + // app thread (along with all the other windows, since X is strictly + // single-threaded), but Windows requires this to be done in draw + // (because once an OpenGL context has been bound in a given thread, it + // cannot subsequently be bound in any other thread, and we have to bind + // a context in open_window()). + + switch (window->get_pipe()->get_preferred_window_thread()) { + case GraphicsPipe::PWT_app: + _app.add_window(_app._window, window); + break; + + case GraphicsPipe::PWT_draw: + draw->add_window(draw->_window, window); + break; + } + + _windows.push_back(window); + } + + // Steal the list, since remove_window() may remove from _new_windows. + new_windows.swap(_new_windows); } + do_resort_windows(); + // We do it twice, to allow both cull and draw to process the window. for (int i = 0; i < 2; ++i) { _app.do_windows(this, current_thread); @@ -970,7 +1016,15 @@ open_windows() { } } - _needs_open_windows = false; + // Now go through the list again to check whether they opened successfully. + for (auto it = new_windows.begin(); it != new_windows.end(); ++it) { + GraphicsOutput *window = *it; + if (window->is_valid()) { + do_add_gsg(window->get_gsg(), window->get_pipe()); + } else { + remove_window(window); + } + } } /** @@ -1927,10 +1981,10 @@ do_draw(GraphicsOutput *win, GraphicsStateGuardian *gsg, DisplayRegion *dr, Thre * list of windows, and to request that the window be opened. */ void GraphicsEngine:: -do_add_window(GraphicsOutput *window, - const GraphicsThreadingModel &threading_model) { - nassertv(window != NULL); - ReMutexHolder holder(_lock); +do_add_window(GraphicsOutput *window) { + nassertv(window != nullptr); + + MutexHolder holder(_new_windows_lock); nassertv(window->get_engine() == this); // We have a special counter that is unique per window that allows us to @@ -1938,50 +1992,13 @@ do_add_window(GraphicsOutput *window, window->_internal_sort_index = _window_sort_index; ++_window_sort_index; - _windows_sorted = false; - _windows.push_back(window); - - WindowRenderer *cull = - get_window_renderer(threading_model.get_cull_name(), - threading_model.get_cull_stage()); - WindowRenderer *draw = - get_window_renderer(threading_model.get_draw_name(), - threading_model.get_draw_stage()); - - if (threading_model.get_cull_sorting()) { - cull->add_window(cull->_cull, window); - draw->add_window(draw->_draw, window); - } else { - cull->add_window(cull->_cdraw, window); - } - -/* - * Ask the pipe which thread it prefers to run its windowing commands in (the - * "window thread"). This is the thread that handles the commands to open, - * resize, etc. the window. X requires this to be done in the app thread - * (along with all the other windows, since X is strictly single-threaded), - * but Windows requires this to be done in draw (because once an OpenGL - * context has been bound in a given thread, it cannot subsequently be bound - * in any other thread, and we have to bind a context in open_window()). - */ - - switch (window->get_pipe()->get_preferred_window_thread()) { - case GraphicsPipe::PWT_app: - _app.add_window(_app._window, window); - break; - - case GraphicsPipe::PWT_draw: - draw->add_window(draw->_window, window); - break; - } - if (display_cat.is_debug()) { display_cat.debug() << "Created " << window->get_type() << " " << (void *)window << "\n"; } window->request_open(); - _needs_open_windows = true; + _new_windows.push_back(window); } /** @@ -1990,13 +2007,12 @@ do_add_window(GraphicsOutput *window, * variables based on the gsg's capabilities. */ void GraphicsEngine:: -do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe, - const GraphicsThreadingModel &threading_model) { +do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe) { nassertv(gsg != NULL); ReMutexHolder holder(_lock); nassertv(gsg->get_pipe() == pipe && gsg->get_engine() == this); - gsg->_threading_model = threading_model; + gsg->_threading_model = _threading_model; if (!_default_loader.is_null()) { gsg->set_loader(_default_loader); } @@ -2004,8 +2020,8 @@ do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe, auto_adjust_capabilities(gsg); WindowRenderer *draw = - get_window_renderer(threading_model.get_draw_name(), - threading_model.get_draw_stage()); + get_window_renderer(_threading_model.get_draw_name(), + _threading_model.get_draw_stage()); draw->add_gsg(gsg); } diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h index d18d86f690..853487d05a 100644 --- a/panda/src/display/graphicsEngine.h +++ b/panda/src/display/graphicsEngine.h @@ -166,10 +166,8 @@ private: void do_draw(GraphicsOutput *win, GraphicsStateGuardian *gsg, DisplayRegion *dr, Thread *current_thread); - void do_add_window(GraphicsOutput *window, - const GraphicsThreadingModel &threading_model); - void do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe, - const GraphicsThreadingModel &threading_model); + void do_add_window(GraphicsOutput *window); + void do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe); void do_remove_window(GraphicsOutput *window, Thread *current_thread); void do_resort_windows(); void terminate_threads(Thread *current_thread); @@ -308,8 +306,11 @@ private: Pipeline *_pipeline; Windows _windows; bool _windows_sorted; + + // This lock protects the next two fields. + Mutex _new_windows_lock; unsigned int _window_sort_index; - bool _needs_open_windows; + pvector _new_windows; WindowRenderer _app; typedef pmap Threads; diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 2d07f2c971..d590aa8d37 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -3231,9 +3231,10 @@ async_reload_texture(TextureContext *tc) { PT(Texture) GraphicsStateGuardian:: get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host) { PandaNode *node = light_np.node(); + bool is_point = node->is_of_type(PointLight::get_class_type()); nassertr(node->is_of_type(DirectionalLight::get_class_type()) || - node->is_of_type(PointLight::get_class_type()) || - node->is_of_type(Spotlight::get_class_type()), NULL); + node->is_of_type(Spotlight::get_class_type()) || + is_point, nullptr); LightLensNode *light = (LightLensNode *)node; if (light == nullptr || !light->_shadow_caster) { @@ -3246,20 +3247,49 @@ get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host) { } } + // The light's shadow map should have been created by set_shadow_caster(). + nassertr(light->_shadow_map != nullptr, nullptr); + // See if we already have a buffer. If not, create one. - if (light->_sbuffers.count(this) == 0) { - if (host == (GraphicsOutputBase *)NULL) { - host = _current_display_region->get_window(); - } - nassertr(host != NULL, NULL); - - // Nope, the light doesn't have a buffer for our GSG. Make one. - return make_shadow_buffer(light_np, host); - - } else { + if (light->_sbuffers.count(this) != 0) { // There's already a buffer - use that. - return light->_sbuffers[this]->get_texture(); + return light->_shadow_map; } + + if (display_cat.is_debug()) { + display_cat.debug() + << "Constructing shadow buffer for light '" << light->get_name() + << "', size=" << light->_sb_size[0] << "x" << light->_sb_size[1] + << ", sort=" << light->_sb_sort << "\n"; + } + + if (host == nullptr) { + nassertr(_current_display_region != nullptr, nullptr); + host = _current_display_region->get_window(); + } + nassertr(host != nullptr, nullptr); + + // Nope, the light doesn't have a buffer for our GSG. Make one. + GraphicsOutput *sbuffer = make_shadow_buffer(light, light->_shadow_map, + DCAST(GraphicsOutput, host)); + + // Assign display region(s) to the buffer and camera + if (is_point) { + for (int i = 0; i < 6; ++i) { + PT(DisplayRegion) dr = sbuffer->make_mono_display_region(0, 1, 0, 1); + dr->set_lens_index(i); + dr->set_target_tex_page(i); + dr->set_camera(light_np); + dr->set_clear_depth_active(true); + } + } else { + PT(DisplayRegion) dr = sbuffer->make_mono_display_region(0, 1, 0, 1); + dr->set_camera(light_np); + dr->set_clear_depth_active(true); + } + + light->_sbuffers[this] = sbuffer; + return light->_shadow_map; } /** @@ -3299,101 +3329,33 @@ get_dummy_shadow_map(Texture::TextureType texture_type) const { } /** - * Creates a depth buffer for shadow mapping. This is a convenience function - * for the ShaderGenerator; putting this directly in the ShaderGenerator would - * cause circular dependency issues. Returns the depth texture. + * Creates a depth buffer for shadow mapping. A derived GSG can override this + * if it knows that a particular buffer type works best for shadow rendering. */ -PT(Texture) GraphicsStateGuardian:: -make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) { - // Make sure everything is valid. - PandaNode *node = light_np.node(); - nassertr(node->is_of_type(DirectionalLight::get_class_type()) || - node->is_of_type(PointLight::get_class_type()) || - node->is_of_type(Spotlight::get_class_type()), NULL); - - LightLensNode *light = (LightLensNode *)node; - if (light == NULL || !light->_shadow_caster) { - return NULL; - } - +GraphicsOutput *GraphicsStateGuardian:: +make_shadow_buffer(LightLensNode *light, Texture *tex, GraphicsOutput *host) { bool is_point = light->is_of_type(PointLight::get_class_type()); - nassertr(light->_sbuffers.count(this) == 0, NULL); - - if (display_cat.is_debug()) { - display_cat.debug() - << "Constructing shadow buffer for light '" << light->get_name() - << "', size=" << light->_sb_size[0] << "x" << light->_sb_size[1] - << ", sort=" << light->_sb_sort << "\n"; - } - // Determine the properties for creating the depth buffer. FrameBufferProperties fbp; fbp.set_depth_bits(shadow_depth_bits); - WindowProperties props = WindowProperties::size(light->_sb_size[0], light->_sb_size[1]); + WindowProperties props = WindowProperties::size(light->_sb_size); int flags = GraphicsPipe::BF_refuse_window; if (is_point) { flags |= GraphicsPipe::BF_size_square; } - // Create the buffer - PT(GraphicsOutput) sbuffer = get_engine()->make_output(get_pipe(), light->get_name(), - light->_sb_sort, fbp, props, flags, this, DCAST(GraphicsOutput, host)); - nassertr(sbuffer != NULL, NULL); + // Create the buffer. This is a bit tricky because make_output() can only + // be called from the app thread, but it won't cause issues as long as the + // pipe can precertify the buffer, which it can in most cases. + GraphicsOutput *sbuffer = get_engine()->make_output(get_pipe(), + light->get_name(), light->_sb_sort, fbp, props, flags, this, host); - // Create a texture and fill it in with some data to workaround an OpenGL - // error - PT(Texture) tex = new Texture(light->get_name()); - if (is_point) { - if (light->_sb_size[0] != light->_sb_size[1]) { - display_cat.error() - << "PointLight shadow buffers must have an equal width and height!\n"; - } - tex->setup_cube_map(light->_sb_size[0], Texture::T_unsigned_byte, Texture::F_depth_component); - } else { - tex->setup_2d_texture(light->_sb_size[0], light->_sb_size[1], Texture::T_unsigned_byte, Texture::F_depth_component); + if (sbuffer != nullptr) { + sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, GraphicsOutput::RTP_depth); } - tex->make_ram_image(); - sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, GraphicsOutput::RTP_depth); - - // Set the wrap mode - if (is_point) { - tex->set_wrap_u(SamplerState::WM_clamp); - tex->set_wrap_v(SamplerState::WM_clamp); - } else { - tex->set_wrap_u(SamplerState::WM_border_color); - tex->set_wrap_v(SamplerState::WM_border_color); - tex->set_border_color(LVecBase4(1, 1, 1, 1)); - } - - // Note: cube map shadow filtering doesn't seem to work in Cg. - if (get_supports_shadow_filter() && !is_point) { - // If we have the ARB_shadow extension, enable shadow filtering. - tex->set_minfilter(SamplerState::FT_shadow); - tex->set_magfilter(SamplerState::FT_shadow); - } else { - tex->set_minfilter(SamplerState::FT_linear); - tex->set_magfilter(SamplerState::FT_linear); - } - - // Assign display region(s) to the buffer and camera - if (is_point) { - for (int i = 0; i < 6; ++i) { - PT(DisplayRegion) dr = sbuffer->make_mono_display_region(0, 1, 0, 1); - dr->set_lens_index(i); - dr->set_target_tex_page(i); - dr->set_camera(light_np); - dr->set_clear_depth_active(true); - } - } else { - PT(DisplayRegion) dr = sbuffer->make_mono_display_region(0, 1, 0, 1); - dr->set_camera(light_np); - dr->set_clear_depth_active(true); - } - light->_sbuffers[this] = sbuffer; - - return tex; + return sbuffer; } /** diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 8a0ca541d0..9b7855dc31 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -424,7 +424,7 @@ public: PT(Texture) get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host=NULL); PT(Texture) get_dummy_shadow_map(Texture::TextureType texture_type) const; - PT(Texture) make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host); + virtual GraphicsOutput *make_shadow_buffer(LightLensNode *light, Texture *tex, GraphicsOutput *host); virtual void ensure_generated_shader(const RenderState *state); diff --git a/panda/src/display/windowProperties.cxx b/panda/src/display/windowProperties.cxx index c0af101b41..298bed4b45 100644 --- a/panda/src/display/windowProperties.cxx +++ b/panda/src/display/windowProperties.cxx @@ -133,6 +133,12 @@ clear_default() { * size is the only property that matters to buffers. */ WindowProperties WindowProperties:: +size(const LVecBase2i &size) { + WindowProperties props; + props.set_size(size); + return props; +} +WindowProperties WindowProperties:: size(int x_size, int y_size) { WindowProperties props; props.set_size(x_size, y_size); diff --git a/panda/src/display/windowProperties.h b/panda/src/display/windowProperties.h index bd5e964f52..16a65c0096 100644 --- a/panda/src/display/windowProperties.h +++ b/panda/src/display/windowProperties.h @@ -52,6 +52,7 @@ PUBLISHED: MAKE_PROPERTY(config_properties, get_config_properties); MAKE_PROPERTY(default, get_default, set_default); + static WindowProperties size(const LVecBase2i &size); static WindowProperties size(int x_size, int y_size); bool operator == (const WindowProperties &other) const; diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index fab18bcb62..ec9d1d603f 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -7561,6 +7561,36 @@ bind_light(Spotlight *light_obj, const NodePath &light, int light_id) { } #endif // SUPPORT_FIXED_FUNCTION +/** + * Creates a depth buffer for shadow mapping. A derived GSG can override this + * if it knows that a particular buffer type works best for shadow rendering. + */ +GraphicsOutput *CLP(GraphicsStateGuardian):: +make_shadow_buffer(LightLensNode *light, Texture *tex, GraphicsOutput *host) { + // We override this to circumvent the fact that GraphicsEngine::make_output + // can only be called from the app thread. + if (!_supports_framebuffer_object) { + return GraphicsStateGuardian::make_shadow_buffer(light, tex, host); + } + + bool is_point = light->is_of_type(PointLight::get_class_type()); + + // Determine the properties for creating the depth buffer. + FrameBufferProperties fbp; + fbp.set_depth_bits(shadow_depth_bits); + + WindowProperties props = WindowProperties::size(light->get_shadow_buffer_size()); + int flags = GraphicsPipe::BF_refuse_window; + if (is_point) { + flags |= GraphicsPipe::BF_size_square; + } + + CLP(GraphicsBuffer) *sbuffer = new GLGraphicsBuffer(get_engine(), get_pipe(), light->get_name(), fbp, props, flags, this, host); + sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, GraphicsOutput::RTP_depth); + get_engine()->add_window(sbuffer, light->get_shadow_buffer_sort()); + return sbuffer; +} + #ifdef SUPPORT_IMMEDIATE_MODE /** * Uses the ImmediateModeSender to draw a series of primitives of the diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index e4da7cade1..2b357ece5b 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -381,6 +381,8 @@ public: int light_id); #endif + virtual GraphicsOutput *make_shadow_buffer(LightLensNode *light, Texture *tex, GraphicsOutput *host); + LVecBase4 get_light_color(Light *light) const; #ifdef SUPPORT_IMMEDIATE_MODE diff --git a/panda/src/pgraphnodes/lightLensNode.I b/panda/src/pgraphnodes/lightLensNode.I index eb2b192f97..1df14eb971 100644 --- a/panda/src/pgraphnodes/lightLensNode.I +++ b/panda/src/pgraphnodes/lightLensNode.I @@ -41,6 +41,9 @@ set_shadow_caster(bool caster) { } _shadow_caster = caster; set_active(caster); + if (caster) { + setup_shadow_map(); + } } /** @@ -65,6 +68,17 @@ set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int buffer_so _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. + */ +INLINE int LightLensNode:: +get_shadow_buffer_sort() const { + return _sb_sort; } /** @@ -82,8 +96,9 @@ INLINE void LightLensNode:: set_shadow_buffer_size(const LVecBase2i &size) { if (size != _sb_size) { clear_shadow_buffers(); + _sb_size = size; + setup_shadow_map(); } - _sb_size = size; } /** diff --git a/panda/src/pgraphnodes/lightLensNode.cxx b/panda/src/pgraphnodes/lightLensNode.cxx index f68b7084ec..b79b5e8dc0 100644 --- a/panda/src/pgraphnodes/lightLensNode.cxx +++ b/panda/src/pgraphnodes/lightLensNode.cxx @@ -67,6 +67,9 @@ LightLensNode(const LightLensNode ©) : _has_specular_color(copy._has_specular_color), _attrib_count(0) { + if (_shadow_caster) { + setup_shadow_map(); + } } /** @@ -75,20 +78,43 @@ LightLensNode(const LightLensNode ©) : */ void LightLensNode:: clear_shadow_buffers() { + if (_shadow_map) { + // Clear it to all ones, so that any shaders that might still be using + // it will see the shadows being disabled. + _shadow_map->clear_image(); + } + ShadowBuffers::iterator it; for(it = _sbuffers.begin(); it != _sbuffers.end(); ++it) { - PT(Texture) tex = (*it).second->get_texture(); - if (tex) { - // Clear it to all ones, so that any shaders that might still be using - // it will see the shadows being disabled. - tex->set_clear_color(LColor(1)); - tex->clear_image(); - } (*it).first->remove_window((*it).second); } _sbuffers.clear(); } +/** + * Creates the shadow map texture. Can be overridden. + */ +void LightLensNode:: +setup_shadow_map() { + if (_shadow_map != nullptr && + _shadow_map->get_x_size() == _sb_size[0] && + _shadow_map->get_y_size() == _sb_size[1]) { + // Nothing to do. + return; + } + + if (_shadow_map == nullptr) { + _shadow_map = new Texture(get_name()); + } + + _shadow_map->setup_2d_texture(_sb_size[0], _sb_size[1], Texture::T_unsigned_byte, Texture::F_depth_component); + _shadow_map->set_clear_color(LColor(1)); + _shadow_map->set_wrap_u(SamplerState::WM_border_color); + _shadow_map->set_wrap_v(SamplerState::WM_border_color); + _shadow_map->set_border_color(LColor(1)); + _shadow_map->set_minfilter(SamplerState::FT_shadow); + _shadow_map->set_magfilter(SamplerState::FT_shadow); +} /** * This is called when the light is added to a LightAttrib. diff --git a/panda/src/pgraphnodes/lightLensNode.h b/panda/src/pgraphnodes/lightLensNode.h index 2102acc541..03c1342927 100644 --- a/panda/src/pgraphnodes/lightLensNode.h +++ b/panda/src/pgraphnodes/lightLensNode.h @@ -41,6 +41,8 @@ PUBLISHED: INLINE void set_shadow_caster(bool caster); INLINE void set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int sort = -10); + INLINE int get_shadow_buffer_sort() const; + INLINE LVecBase2i get_shadow_buffer_size() const; INLINE void set_shadow_buffer_size(const LVecBase2i &size); @@ -53,12 +55,15 @@ PUBLISHED: protected: LightLensNode(const LightLensNode ©); void clear_shadow_buffers(); + virtual void setup_shadow_map(); LVecBase2i _sb_size; bool _shadow_caster; bool _has_specular_color; int _sb_sort; + PT(Texture) _shadow_map; + // This is really a map of GSG -> GraphicsOutput. typedef pmap ShadowBuffers; ShadowBuffers _sbuffers; @@ -106,7 +111,6 @@ private: static TypeHandle _type_handle; friend class GraphicsStateGuardian; - friend class ShaderGenerator; }; INLINE ostream &operator << (ostream &out, const LightLensNode &light) { diff --git a/panda/src/pgraphnodes/pointLight.cxx b/panda/src/pgraphnodes/pointLight.cxx index caa8d86261..02cc33fda9 100644 --- a/panda/src/pgraphnodes/pointLight.cxx +++ b/panda/src/pgraphnodes/pointLight.cxx @@ -17,6 +17,7 @@ #include "bamReader.h" #include "datagram.h" #include "datagramIterator.h" +#include "config_pgraphnodes.h" TypeHandle PointLight::_type_handle; @@ -184,6 +185,35 @@ bind(GraphicsStateGuardianBase *gsg, const NodePath &light, int light_id) { gsg->bind_light(this, light, light_id); } +/** + * Creates the shadow map texture. Can be overridden. + */ +void PointLight:: +setup_shadow_map() { + if (_shadow_map != nullptr && _shadow_map->get_x_size() == _sb_size[0]) { + // Nothing to do. + return; + } + + if (_sb_size[0] != _sb_size[1]) { + pgraphnodes_cat.error() + << "PointLight shadow buffers must have an equal width and height!\n"; + } + + if (_shadow_map == nullptr) { + _shadow_map = new Texture(get_name()); + } + + _shadow_map->setup_cube_map(_sb_size[0], Texture::T_unsigned_byte, Texture::F_depth_component); + _shadow_map->set_clear_color(LColor(1)); + _shadow_map->set_wrap_u(SamplerState::WM_clamp); + _shadow_map->set_wrap_v(SamplerState::WM_clamp); + + // Note: cube map shadow filtering doesn't seem to work in Cg. + _shadow_map->set_minfilter(SamplerState::FT_linear); + _shadow_map->set_magfilter(SamplerState::FT_linear); +} + /** * Tells the BamReader how to create objects of type PointLight. */ diff --git a/panda/src/pgraphnodes/pointLight.h b/panda/src/pgraphnodes/pointLight.h index cbfcf9b50d..ba3a334d47 100644 --- a/panda/src/pgraphnodes/pointLight.h +++ b/panda/src/pgraphnodes/pointLight.h @@ -63,6 +63,8 @@ public: int light_id); private: + virtual void setup_shadow_map(); + // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA_PGRAPHNODES CData : public CycleData { public: From a6f737d4eb38731c3a1312c99a69e9c7086c466a Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 31 Dec 2017 09:30:32 +0100 Subject: [PATCH 010/118] linmath: make raising quaternion to power more meaningful Fixes #160 --- panda/src/linmath/lquaternion_src.cxx | 23 +++++++++++++++++++++++ panda/src/linmath/lquaternion_src.h | 2 ++ 2 files changed, 25 insertions(+) diff --git a/panda/src/linmath/lquaternion_src.cxx b/panda/src/linmath/lquaternion_src.cxx index 7ffb7766a3..cd48827eea 100644 --- a/panda/src/linmath/lquaternion_src.cxx +++ b/panda/src/linmath/lquaternion_src.cxx @@ -26,6 +26,29 @@ pure_imaginary(const FLOATNAME(LVector3) &v) { return FLOATNAME(LQuaternion)(0, v[0], v[1], v[2]); } +/** + * Returns a new quaternion that represents this quaternion raised to the + * given power. + */ +FLOATNAME(LQuaternion) FLOATNAME(LQuaternion):: +__pow__(FLOATTYPE power) const { + if (IS_NEARLY_ZERO(power)) { + return FLOATNAME(LQuaternion)(1, 0, 0, 0); + } + + FLOATTYPE l = length(); + FLOATTYPE norm = _v(0) / l; + if (IS_NEARLY_EQUAL(cabs(norm), (FLOATTYPE)1)) { + return FLOATNAME(LQuaternion)(cpow(_v(0), power), 0, 0, 0); + } + + FLOATTYPE angle = acos(norm); + FLOATTYPE angle2 = angle * power; + FLOATTYPE mag = cpow(l, power - 1); + FLOATTYPE mult = mag * (sin(angle2) / sin(angle)); + return FLOATNAME(LQuaternion)(cos(angle2) * mag * l, _v(1) * mult, _v(2) * mult, _v(3) * mult); +} + /** * Based on the quat lib from VRPN. */ diff --git a/panda/src/linmath/lquaternion_src.h b/panda/src/linmath/lquaternion_src.h index 429b86a0cc..00ba2a934f 100644 --- a/panda/src/linmath/lquaternion_src.h +++ b/panda/src/linmath/lquaternion_src.h @@ -57,6 +57,8 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LMatrix3) operator *(const FLOATNAME(LMatrix3) &); INLINE_LINMATH FLOATNAME(LMatrix4) operator *(const FLOATNAME(LMatrix4) &); + FLOATNAME(LQuaternion) __pow__(FLOATTYPE) const; + INLINE_LINMATH bool almost_equal( const FLOATNAME(LQuaternion) &other) const; INLINE_LINMATH bool almost_equal( From a96b171095d9874620e5059b65a5324bed5d0e9e Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 31 Dec 2017 09:51:25 +0100 Subject: [PATCH 011/118] glgsg: only consider texture/texgen in munger if !gl_parallel_arrays This could possibly be used to workaround issue #218 --- panda/src/glstuff/glGeomMunger_src.cxx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/panda/src/glstuff/glGeomMunger_src.cxx b/panda/src/glstuff/glGeomMunger_src.cxx index b26f2bbb10..5a1649f832 100644 --- a/panda/src/glstuff/glGeomMunger_src.cxx +++ b/panda/src/glstuff/glGeomMunger_src.cxx @@ -23,13 +23,8 @@ ALLOC_DELETED_CHAIN_DEF(CLP(GeomMunger)); CLP(GeomMunger):: CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state) : StandardMunger(gsg, state, 4, NT_uint8, C_color), - _texture((const TextureAttrib *)state->get_attrib(TextureAttrib::get_class_slot())), - _tex_gen((const TexGenAttrib *)state->get_attrib(TexGenAttrib::get_class_slot())) -{ - // Set a callback to unregister ourselves when either the Texture or the - // TexGen object gets deleted. - _texture.set_callback(this); - _tex_gen.set_callback(this); + _texture(nullptr), + _tex_gen(nullptr) { _flags = 0; @@ -38,6 +33,15 @@ CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state) : } else if (gl_parallel_arrays) { _flags |= F_parallel_arrays; } + + if ((_flags & F_parallel_arrays) == 0) { + // Set a callback to unregister ourselves when either the Texture or the + // TexGen object gets deleted. + _texture = (const TextureAttrib *)state->get_attrib(TextureAttrib::get_class_slot()); + _tex_gen = (const TexGenAttrib *)state->get_attrib(TexGenAttrib::get_class_slot()); + _texture.set_callback(this); + _tex_gen.set_callback(this); + } } /** From 84bdce01f291fb9b19b64655dda3265bb0a4252b Mon Sep 17 00:00:00 2001 From: Younguk Kim Date: Mon, 1 Jan 2018 18:14:48 +0900 Subject: [PATCH 012/118] Fix crash by end iterator while closing a window --- panda/src/display/graphicsEngine.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index 1a38089e01..eca453900e 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -529,7 +529,7 @@ remove_window(GraphicsOutput *window) { // Also check whether it is in _new_windows. { MutexHolder new_windows_holder(_new_windows_lock, current_thread); - _new_windows.erase(std::remove(_new_windows.begin(), _new_windows.end(), ptwin)); + _new_windows.erase(std::remove(_new_windows.begin(), _new_windows.end(), ptwin), _new_windows.end()); } if (count == 0) { From 92787264ded2e97e469f00ff4aba19e970aa4556 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 2 Jan 2018 17:11:27 +0100 Subject: [PATCH 013/118] putil: fix assertion when global clock is created on non-main thread --- panda/src/putil/clockObject.I | 6 ++++-- panda/src/putil/clockObject.cxx | 21 ++++++++++----------- panda/src/putil/clockObject.h | 4 ++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/panda/src/putil/clockObject.I b/panda/src/putil/clockObject.I index d9bcac3a7c..f8a0bbc02b 100644 --- a/panda/src/putil/clockObject.I +++ b/panda/src/putil/clockObject.I @@ -213,10 +213,12 @@ check_errors(Thread *current_thread) { */ INLINE ClockObject *ClockObject:: get_global_clock() { - if (_global_clock == (ClockObject *)NULL) { + ClockObject *clock = (ClockObject *)AtomicAdjust::get_ptr(_global_clock); + if (UNLIKELY(clock == nullptr)) { make_global_clock(); + clock = (ClockObject *)_global_clock; } - return _global_clock; + return clock; } /** diff --git a/panda/src/putil/clockObject.cxx b/panda/src/putil/clockObject.cxx index d53f4ea329..09353ab739 100644 --- a/panda/src/putil/clockObject.cxx +++ b/panda/src/putil/clockObject.cxx @@ -21,21 +21,16 @@ void (*ClockObject::_start_clock_wait)() = ClockObject::dummy_clock_wait; void (*ClockObject::_start_clock_busy_wait)() = ClockObject::dummy_clock_wait; void (*ClockObject::_stop_clock_wait)() = ClockObject::dummy_clock_wait; -ClockObject *ClockObject::_global_clock; +AtomicAdjust::Pointer ClockObject::_global_clock = nullptr; TypeHandle ClockObject::_type_handle; /** * */ ClockObject:: -ClockObject() : _ticks(get_class_type()) { +ClockObject(Mode mode) : _ticks(get_class_type()), _mode(mode) { _true_clock = TrueClock::get_global_ptr(); - // Each clock except for the application global clock is created in M_normal - // mode. The application global clock is later reset to respect clock_mode, - // which comes from the Config.prc file. - _mode = M_normal; - _start_short_time = _true_clock->get_short_time(); _start_long_time = _true_clock->get_long_time(); _actual_frame_time = 0.0; @@ -523,7 +518,7 @@ wait_until(double want_time) { */ void ClockObject:: make_global_clock() { - nassertv(_global_clock == (ClockObject *)NULL); + nassertv(_global_clock == nullptr); ConfigVariableEnum clock_mode ("clock-mode", ClockObject::M_normal, @@ -532,9 +527,13 @@ make_global_clock() { "effects like simulated reduced frame rate. See " "ClockObject::set_mode().")); - _global_clock = new ClockObject; - _global_clock->set_mode(clock_mode); - _global_clock->ref(); + ClockObject *clock = new ClockObject(clock_mode); + clock->local_object(); + + if (AtomicAdjust::compare_and_exchange_ptr(_global_clock, nullptr, clock) != nullptr) { + // Another thread beat us to it. + delete clock; + } } /** diff --git a/panda/src/putil/clockObject.h b/panda/src/putil/clockObject.h index ca7e3d2116..bcea511f29 100644 --- a/panda/src/putil/clockObject.h +++ b/panda/src/putil/clockObject.h @@ -68,7 +68,7 @@ PUBLISHED: M_integer_limited, }; - ClockObject(); + ClockObject(Mode mode = M_normal); ClockObject(const ClockObject ©); INLINE ~ClockObject(); @@ -172,7 +172,7 @@ private: typedef CycleDataWriter CDWriter; typedef CycleDataStageReader CDStageReader; - static ClockObject *_global_clock; + static AtomicAdjust::Pointer _global_clock; public: static TypeHandle get_class_type() { From 7ea16fd77269a46b2c4222557ec861ae965f15d0 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 2 Jan 2018 19:07:43 +0100 Subject: [PATCH 014/118] putil: fix copy constructor of SimpleHashMap --- panda/src/putil/simpleHashMap.I | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/panda/src/putil/simpleHashMap.I b/panda/src/putil/simpleHashMap.I index 78d4bdf2bd..26728f1e01 100644 --- a/panda/src/putil/simpleHashMap.I +++ b/panda/src/putil/simpleHashMap.I @@ -41,7 +41,13 @@ SimpleHashMap(const SimpleHashMap ©) : _deleted_chain = memory_hook->get_deleted_chain(alloc_size); _table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none()); - memcpy(_table, copy._table, alloc_size); + + for (size_t i = 0; i < _num_entries; ++i) { + new(&_table[i]) TableEntry(copy._table[i]); + } + + // Copy the index array. + memcpy(get_index_array(), copy.get_index_array(), _table_size * sizeof(int) * sparsity); } /** @@ -88,7 +94,12 @@ operator = (const SimpleHashMap ©) { _deleted_chain = memory_hook->get_deleted_chain(alloc_size); _table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none()); - memcpy(_table, copy._table, alloc_size); + for (size_t i = 0; i < _num_entries; ++i) { + new(&_table[i]) TableEntry(copy._table[i]); + } + + // Copy the index array. + memcpy(get_index_array(), copy.get_index_array(), _table_size * sizeof(int) * sparsity); } return *this; } From f37c070c7cbbc1ebf729fdbcc882e190a6d0cd84 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 3 Jan 2018 00:06:37 +0100 Subject: [PATCH 015/118] tests: cleanup GraphicsEngine to prevent shutdown freeze on windows This occurs with the multithreaded render pipeline. It's not an issue when using ShowBase because ShowBase also explicitly calls this during shutdown. --- tests/display/conftest.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/display/conftest.py b/tests/display/conftest.py index 474ab5ee0b..c66ef2f54a 100644 --- a/tests/display/conftest.py +++ b/tests/display/conftest.py @@ -20,6 +20,9 @@ def graphics_engine(): engine = GraphicsEngine.get_global_ptr() yield engine + # This causes GraphicsEngine to also terminate the render threads. + engine.remove_all_windows() + @pytest.fixture def window(graphics_pipe, graphics_engine): From 512f305c12a6977d710b23eb7da620995c4e5aa3 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 3 Jan 2018 00:09:30 +0100 Subject: [PATCH 016/118] display: fix assertions when removing window that isn't open yet --- panda/src/display/graphicsEngine.cxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index eca453900e..e7ea4acd78 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -529,7 +529,11 @@ remove_window(GraphicsOutput *window) { // Also check whether it is in _new_windows. { MutexHolder new_windows_holder(_new_windows_lock, current_thread); + size_t old_size = _new_windows.size(); _new_windows.erase(std::remove(_new_windows.begin(), _new_windows.end(), ptwin), _new_windows.end()); + if (count == 0 && _new_windows.size() < old_size) { + count = 1; + } } if (count == 0) { From 4bac9efffef41be4837435e0f11888a7c84178cd Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 3 Jan 2018 00:10:24 +0100 Subject: [PATCH 017/118] display: fix dispatch_compute and extract_texture_data in MT pipeline This is required to make the test suite run with pipelining enabled. It's necessary to let the draw thread do these tasks because that's where the OpenGL context is bound to. However, we need to let it access the data from the calling thread, so we have to temporarily change the pipeline stage of the draw thread. I'm not really happy about this solution; it would be better to temporarily make the context current to the app thread, but we need a window for that, which we don't currently require to be passed into that method. --- panda/src/display/graphicsEngine.cxx | 74 ++++++++++++++++++++++------ panda/src/display/graphicsEngine.h | 9 ++++ 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index e7ea4acd78..874ab9be13 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -1118,17 +1118,34 @@ extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg) { // has finished its current task. WindowRenderer *wr = get_window_renderer(draw_name, 0); RenderThread *thread = (RenderThread *)wr; - MutexHolder holder2(thread->_cv_mutex); + MutexHolder cv_holder(thread->_cv_mutex); while (thread->_thread_state != TS_wait) { thread->_cv_done.wait(); } - // OK, now the draw thread is idle. That's really good enough for our - // purposes; we don't *actually* need to make the draw thread do the work - // --it's sufficient that it's not doing anything else while we access the - // GSG. - return gsg->extract_texture_data(tex); + // Temporarily set this so that it accesses data from the current thread. + int pipeline_stage = Thread::get_current_pipeline_stage(); + int draw_pipeline_stage = thread->get_pipeline_stage(); + thread->set_pipeline_stage(pipeline_stage); + + // Now that the draw thread is idle, signal it to do the extraction task. + thread->_gsg = gsg; + thread->_texture = tex; + thread->_thread_state = TS_do_extract; + thread->_cv_start.notify(); + thread->_cv_mutex.release(); + thread->_cv_mutex.acquire(); + + //XXX is this necessary, or is acquiring the mutex enough? + while (thread->_thread_state != TS_wait) { + thread->_cv_done.wait(); + } + + thread->set_pipeline_stage(draw_pipeline_stage); + thread->_gsg = nullptr; + thread->_texture = nullptr; + return thread->_result; } } @@ -1148,6 +1165,7 @@ extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg) { void GraphicsEngine:: dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, GraphicsStateGuardian *gsg) { nassertv(sattr->get_shader() != (Shader *)NULL); + nassertv(gsg != nullptr); ReMutexHolder holder(_lock); @@ -1156,26 +1174,43 @@ dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, Graph string draw_name = gsg->get_threading_model().get_draw_name(); if (draw_name.empty()) { // A single-threaded environment. No problem. + gsg->set_state_and_transform(state, TransformState::make_identity()); + gsg->dispatch_compute(work_groups[0], work_groups[1], work_groups[2]); } else { // A multi-threaded environment. We have to wait until the draw thread // has finished its current task. WindowRenderer *wr = get_window_renderer(draw_name, 0); RenderThread *thread = (RenderThread *)wr; - MutexHolder holder2(thread->_cv_mutex); + MutexHolder cv_holder(thread->_cv_mutex); while (thread->_thread_state != TS_wait) { thread->_cv_done.wait(); } - // OK, now the draw thread is idle. That's really good enough for our - // purposes; we don't *actually* need to make the draw thread do the work - // --it's sufficient that it's not doing anything else while we access the - // GSG. - } + // Temporarily set this so that it accesses data from the current thread. + int pipeline_stage = Thread::get_current_pipeline_stage(); + int draw_pipeline_stage = thread->get_pipeline_stage(); + thread->set_pipeline_stage(pipeline_stage); - gsg->set_state_and_transform(state, TransformState::make_identity()); - gsg->dispatch_compute(work_groups[0], work_groups[1], work_groups[2]); + // Now that the draw thread is idle, signal it to do the compute task. + thread->_gsg = gsg; + thread->_state = state.p(); + thread->_work_groups = work_groups; + thread->_thread_state = TS_do_compute; + thread->_cv_start.notify(); + thread->_cv_mutex.release(); + thread->_cv_mutex.acquire(); + + //XXX is this necessary, or is acquiring the mutex enough? + while (thread->_thread_state != TS_wait) { + thread->_cv_done.wait(); + } + + thread->set_pipeline_stage(draw_pipeline_stage); + thread->_gsg = nullptr; + thread->_state = nullptr; + } } /** @@ -2584,6 +2619,17 @@ thread_main() { do_pending(_engine, current_thread); break; + case TS_do_compute: + nassertd(_gsg != nullptr && _state != nullptr) break; + _gsg->set_state_and_transform(_state, TransformState::make_identity()); + _gsg->dispatch_compute(_work_groups[0], _work_groups[1], _work_groups[2]); + break; + + case TS_do_extract: + nassertd(_gsg != nullptr && _texture != nullptr) break; + _result = _gsg->extract_texture_data(_texture); + break; + case TS_terminate: do_pending(_engine, current_thread); do_close(_engine, current_thread); diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h index 853487d05a..c21bec1d73 100644 --- a/panda/src/display/graphicsEngine.h +++ b/panda/src/display/graphicsEngine.h @@ -123,6 +123,8 @@ public: TS_do_flip, TS_do_release, TS_do_windows, + TS_do_compute, + TS_do_extract, TS_terminate, TS_done }; @@ -299,6 +301,13 @@ private: ConditionVar _cv_start; ConditionVar _cv_done; ThreadState _thread_state; + + // These are stored for extract_texture_data and dispatch_compute. + GraphicsStateGuardian *_gsg; + Texture *_texture; + const RenderState *_state; + LVecBase3i _work_groups; + bool _result; }; WindowRenderer *get_window_renderer(const string &name, int pipeline_stage); From ad58d5161fd2ad067c6c2d78f3ff6490e076bf53 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 3 Jan 2018 12:24:31 +0100 Subject: [PATCH 018/118] installpanda: set proper permissions on includes/man pages Fixes: #159 --- makepanda/installpanda.py | 8 ++++++++ makepanda/makepanda.py | 8 ++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/makepanda/installpanda.py b/makepanda/installpanda.py index fa9c744927..98839315e4 100644 --- a/makepanda/installpanda.py +++ b/makepanda/installpanda.py @@ -233,6 +233,14 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir( DeleteBuildFiles(destdir+prefix+"/include/panda3d") DeleteEmptyDirs(destdir+prefix+"/include/panda3d") + # Change permissions on include directory. + os.chmod(destdir + prefix + "/include", 0o755) + for root, dirs, files in os.walk(destdir + prefix + "/include"): + for basename in dirs: + os.chmod(os.path.join(root, basename), 0o755) + for basename in files: + os.chmod(os.path.join(root, basename), 0o644) + # rpmlint doesn't like this file, for some reason. if (os.path.isfile(destdir+prefix+"/share/panda3d/direct/leveleditor/copyfiles.pl")): os.remove(destdir+prefix+"/share/panda3d/direct/leveleditor/copyfiles.pl") diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 4bd471ed37..291bfc2cb4 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -6654,8 +6654,8 @@ def MakeInstallerLinux(): else: InstallPanda(destdir="targetroot", prefix="/usr", outputdir=GetOutputDir(), libdir=lib_dir) oscmd("chmod -R 755 targetroot/usr/share/panda3d") - oscmd("mkdir -p targetroot/usr/share/man/man1") - oscmd("cp doc/man/*.1 targetroot/usr/share/man/man1/") + oscmd("mkdir -m 0755 -p targetroot/usr/share/man/man1") + oscmd("install -m 0644 doc/man/*.1 targetroot/usr/share/man/man1/") oscmd("dpkg --print-architecture > "+GetOutputDir()+"/tmp/architecture.txt") pkg_arch = ReadFile(GetOutputDir()+"/tmp/architecture.txt").strip() @@ -6815,8 +6815,8 @@ def MakeInstallerOSX(): # Trailing newline is important, works around a bug in OSX WriteFile("dstroot/tools/etc/paths.d/Panda3D", "/Developer/Panda3D/bin\n") - oscmd("mkdir -p dstroot/tools/usr/local/share/man/man1") - oscmd("cp doc/man/*.1 dstroot/tools/usr/local/share/man/man1/") + oscmd("mkdir -m 0755 -p dstroot/tools/usr/local/share/man/man1") + oscmd("install -m 0644 doc/man/*.1 dstroot/tools/usr/local/share/man/man1/") for base in os.listdir(GetOutputDir()+"/bin"): binname = "dstroot/tools/Developer/Panda3D/bin/" + base From 9065f2e16e0b6c4e2c4ec811be4c26cd0f63f7f7 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 3 Jan 2018 14:09:15 +0100 Subject: [PATCH 019/118] installpanda: add --verbose flag --- makepanda/installpanda.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/makepanda/installpanda.py b/makepanda/installpanda.py index 4fb92c4ca0..63a4601ee6 100644 --- a/makepanda/installpanda.py +++ b/makepanda/installpanda.py @@ -284,6 +284,7 @@ if (__name__ == "__main__"): parser.add_option('', '--destdir', dest = 'destdir', help = 'Destination directory [default=%s]' % destdir, default = destdir) parser.add_option('', '--prefix', dest = 'prefix', help = 'Prefix [default=/usr/local]', default = '/usr/local') parser.add_option('', '--runtime', dest = 'runtime', help = 'Specify if runtime build [default=no]', action = 'store_true', default = False) + parser.add_option('', '--verbose', dest = 'verbose', help = 'Print commands that are executed [default=no]', action = 'store_true', default = False) (options, args) = parser.parse_args() destdir = options.destdir @@ -294,6 +295,9 @@ if (__name__ == "__main__"): if (destdir != "" and not os.path.isdir(destdir)): exit("Directory '%s' does not exist!" % destdir) + if options.verbose: + SetVerbose(True) + if (options.runtime): print("Installing Panda3D Runtime into " + destdir + options.prefix) InstallRuntime(destdir = destdir, prefix = options.prefix, outputdir = options.outputdir) From 4f50f6abd007ab98f644f9ccd55e2bfd82506244 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 3 Jan 2018 14:23:13 +0100 Subject: [PATCH 020/118] showbase: use ShowBaseGlobal module as alternative to builtin scope Allows accessing `base` object and several other builtins from the ShowBaseGlobal module. It doesn't bother with builtins that are available as members of the base object such as `render` and `loader`. Fixes #182 --- direct/src/showbase/ShowBase.py | 7 +++++++ direct/src/showbase/ShowBaseGlobal.py | 27 ++++++++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 99a8b77abf..af14d94de6 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -389,6 +389,10 @@ class ShowBase(DirectObject.DirectObject): builtins.aspect2dp = self.aspect2dp builtins.pixel2dp = self.pixel2dp + # Now add this instance to the ShowBaseGlobal module scope. + from . import ShowBaseGlobal + ShowBaseGlobal.base = self + if self.__dev__: ShowBase.notify.debug('__dev__ == %s' % self.__dev__) else: @@ -514,6 +518,9 @@ class ShowBase(DirectObject.DirectObject): del builtins.base del builtins.loader del builtins.taskMgr + ShowBaseGlobal = sys.modules.get('direct.showbase.ShowBaseGlobal', None) + if ShowBaseGlobal: + del ShowBaseGlobal.base # [gjeon] restore sticky key settings if self.config.GetBool('disable-sticky-keys', 0): diff --git a/direct/src/showbase/ShowBaseGlobal.py b/direct/src/showbase/ShowBaseGlobal.py index cf601883cb..1f1c81af1a 100644 --- a/direct/src/showbase/ShowBaseGlobal.py +++ b/direct/src/showbase/ShowBaseGlobal.py @@ -1,14 +1,26 @@ -"""instantiate global ShowBase object""" +"""This module serves as a container to hold the global ShowBase instance, as +an alternative to using the builtin scope. + +Note that you cannot directly import `base` from this module since ShowBase +may not have been created yet; instead, ShowBase dynamically adds itself to +this module's scope when instantiated.""" __all__ = [] -from .ShowBase import * +from .ShowBase import ShowBase, WindowControls +from direct.directnotify.DirectNotifyGlobal import directNotify, giveNotify +from panda3d.core import VirtualFileSystem, Notify, ClockObject, PandaSystem +from panda3d.core import ConfigPageManager, ConfigVariableManager +from panda3d.direct import get_config_showbase -# Create the showbase instance -# This should be created by the game specific "start" file -#ShowBase() -# Instead of creating a show base, assert that one has already been created -assert base +config = get_config_showbase() + +vfs = VirtualFileSystem.getGlobalPtr() +ostream = Notify.out() +globalClock = ClockObject.getGlobalClock() +cpMgr = ConfigPageManager.getGlobalPtr() +cvMgr = ConfigVariableManager.getGlobalPtr() +pandaSystem = PandaSystem.getGlobalPtr() # Set direct notify categories now that we have config directNotify.setDconfigLevels() @@ -31,3 +43,4 @@ builtins.inspect = inspect if (not __debug__) and __dev__: notify = directNotify.newCategory('ShowBaseGlobal') notify.error("You must set 'want-dev' to false in non-debug mode.") + del notify From 1073f5abded8b7c406945bf502a203211778f8d8 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 3 Jan 2018 14:29:03 +0100 Subject: [PATCH 021/118] pgraph: sort "off" texture stages when loading from .bam This fixes an issue where states loaded from .bam file containing more than one "off" texture stage would not compose properly. Fixes #179 --- panda/src/pgraph/textureAttrib.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/panda/src/pgraph/textureAttrib.cxx b/panda/src/pgraph/textureAttrib.cxx index 9b30906e1f..ee3c5a8165 100644 --- a/panda/src/pgraph/textureAttrib.cxx +++ b/panda/src/pgraph/textureAttrib.cxx @@ -825,6 +825,7 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) { } } _on_stages.sort(); + _off_stages.sort(); _sort_seq = UpdateSeq::old(); _filtered_seq = UpdateSeq::old(); From 5980b0b9abb77c7a2a5b4709de0255fb6db03a48 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 5 Jan 2018 13:37:00 +0100 Subject: [PATCH 022/118] openal: fix warning spam every frame in newer AL implementations This fixes error messages being generated of the form: AL lib: (WW) alSetError: Error generated on context 0x801cf8800, code 0xa003 This is caused by alSourceUnqueueBuffers being called without first checking whether processed buffers are available using alGetSourcei. Fixes #180 --- panda/src/audiotraits/openalAudioSound.cxx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/panda/src/audiotraits/openalAudioSound.cxx b/panda/src/audiotraits/openalAudioSound.cxx index da379bfd5c..9abaf24036 100644 --- a/panda/src/audiotraits/openalAudioSound.cxx +++ b/panda/src/audiotraits/openalAudioSound.cxx @@ -435,7 +435,11 @@ pull_used_buffers() { ReMutexHolder holder(OpenALAudioManager::_lock); while (_stream_queued.size()) { ALuint buffer = 0; - alGetError(); + ALint num_buffers = 0; + alGetSourcei(_source, AL_BUFFERS_PROCESSED, &num_buffers); + if (num_buffers <= 0) { + break; + } alSourceUnqueueBuffers(_source, 1, &buffer); int err = alGetError(); if (err == AL_NO_ERROR) { From 47c7ff42cc768ef0df108fae9d24298b9107095a Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 5 Jan 2018 14:15:22 +0100 Subject: [PATCH 023/118] CommonFilters: better handle failure condition when shaders not supported --- direct/src/filter/CommonFilters.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/direct/src/filter/CommonFilters.py b/direct/src/filter/CommonFilters.py index 493f2d9794..894b57c321 100644 --- a/direct/src/filter/CommonFilters.py +++ b/direct/src/filter/CommonFilters.py @@ -131,6 +131,9 @@ class CommonFilters: if (len(configuration) == 0): return + if not self.manager.win.gsg.getSupportsBasicShaders(): + return False + auxbits = 0 needtex = set(["color"]) needtexcoord = set(["color"]) @@ -338,7 +341,10 @@ class CommonFilters: text += " o_color = float4(1, 1, 1, 1) - o_color;\n" text += "}\n" - self.finalQuad.setShader(Shader.make(text, Shader.SL_Cg)) + shader = Shader.make(text, Shader.SL_Cg) + if not shader: + return False + self.finalQuad.setShader(shader) for tex in self.textures: self.finalQuad.setShaderInput("tx"+tex, self.textures[tex]) From 428c5cbbf3d4dab1a2aec69bfaa9594a237f6450 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 5 Jan 2018 14:34:03 +0100 Subject: [PATCH 024/118] gles2gsg: fix compilation error with OpenGL ES 2 --- panda/src/glstuff/glGraphicsStateGuardian_src.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index ec9d1d603f..af84e2bafd 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -7585,7 +7585,7 @@ make_shadow_buffer(LightLensNode *light, Texture *tex, GraphicsOutput *host) { flags |= GraphicsPipe::BF_size_square; } - CLP(GraphicsBuffer) *sbuffer = new GLGraphicsBuffer(get_engine(), get_pipe(), light->get_name(), fbp, props, flags, this, host); + CLP(GraphicsBuffer) *sbuffer = new CLP(GraphicsBuffer)(get_engine(), get_pipe(), light->get_name(), fbp, props, flags, this, host); sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, GraphicsOutput::RTP_depth); get_engine()->add_window(sbuffer, light->get_shadow_buffer_sort()); return sbuffer; From c1679472cd338b147ed2c11043be08e7cffafb19 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 5 Jan 2018 14:39:39 +0100 Subject: [PATCH 025/118] actor: change parent of exposed joint to character This prevents exposed joints from not being affected by a transform directly on the Character node. See panda3d/panda3d#221 for more details. Fixes #221 --- direct/src/actor/Actor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/direct/src/actor/Actor.py b/direct/src/actor/Actor.py index 8a7ef093b0..b9ba299aaa 100644 --- a/direct/src/actor/Actor.py +++ b/direct/src/actor/Actor.py @@ -1103,8 +1103,8 @@ class Actor(DirectObject, NodePath): # Get a handle to the joint. joint = bundle.findChild(jointName) - if node == None: - node = self.attachNewNode(jointName) + if node is None: + node = partDef.partBundleNP.attachNewNode(jointName) if (joint): if localTransform: From 27fb1a4a9e5a0b7e216c037cebed5aa6b3a3afef Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 5 Jan 2018 15:00:21 +0100 Subject: [PATCH 026/118] showbase: fix aspect ratio calculation backs out part of 7a46b2ca60aacc1b8f1c87a7a101546bd6214120, from PR #204 --- direct/src/showbase/ShowBase.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index af14d94de6..26685a0693 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -1267,8 +1267,7 @@ class ShowBase(DirectObject.DirectObject): if win == None: win = self.win - if win != None and win.getSideBySideStereo() and \ - win.hasSize() and win.getSbsLeftYSize() != 0: + if win != None and win.hasSize() and win.getSbsLeftYSize() != 0: aspectRatio = float(win.getSbsLeftXSize()) / float(win.getSbsLeftYSize()) else: if win == None or not hasattr(win, "getRequestedProperties"): From 42f8978236147d8743a6a75190e465fa6b1f862c Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 3 Jan 2018 14:29:03 +0100 Subject: [PATCH 027/118] pgraph: sort "off" texture stages when loading from .bam This fixes an issue where states loaded from .bam file containing more than one "off" texture stage would not compose properly. Fixes #179 --- panda/src/pgraph/textureAttrib.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/panda/src/pgraph/textureAttrib.cxx b/panda/src/pgraph/textureAttrib.cxx index bb7a4808e5..8bc4ed3604 100644 --- a/panda/src/pgraph/textureAttrib.cxx +++ b/panda/src/pgraph/textureAttrib.cxx @@ -911,6 +911,7 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) { } } _on_stages.sort(); + _off_stages.sort(); _sort_seq = UpdateSeq::old(); _filtered_seq = UpdateSeq::old(); From a1b5c6faa2f0183679275ba4ff3acc3926aeb47a Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 26 Nov 2017 23:36:50 +0100 Subject: [PATCH 028/118] makepanda: look for vorbis library without _static suffix Fixes: #202 --- makepanda/makepanda.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 291bfc2cb4..d37daf7068 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -651,9 +651,11 @@ if (COMPILER == "MSVC"): DefSymbol("WX", "_UNICODE", "") DefSymbol("WX", "UNICODE", "") if (PkgSkip("VORBIS")==0): - LibName("VORBIS", GetThirdpartyDir() + "vorbis/lib/libogg_static.lib") - LibName("VORBIS", GetThirdpartyDir() + "vorbis/lib/libvorbis_static.lib") - LibName("VORBIS", GetThirdpartyDir() + "vorbis/lib/libvorbisfile_static.lib") + for lib in ('ogg', 'vorbis', 'vorbisfile'): + path = GetThirdpartyDir() + "vorbis/lib/lib{0}_static.lib".format(lib) + if not os.path.isfile(path): + path = GetThirdpartyDir() + "vorbis/lib/{0}.lib".format(lib) + LibName("VORBIS", path) for pkg in MAYAVERSIONS: if (PkgSkip(pkg)==0): LibName(pkg, '"' + SDK[pkg] + '/lib/Foundation.lib"') From eaf3983c0641c7cace9e941abe12f5142478971d Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 5 Jan 2018 15:40:59 +0100 Subject: [PATCH 029/118] ode: fix trimeshes in double-precision ODE builds Fixes #174 --- panda/src/ode/odeTriMeshData.cxx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/panda/src/ode/odeTriMeshData.cxx b/panda/src/ode/odeTriMeshData.cxx index c78d09fc6d..104ba6daf2 100644 --- a/panda/src/ode/odeTriMeshData.cxx +++ b/panda/src/ode/odeTriMeshData.cxx @@ -103,6 +103,7 @@ OdeTriMeshData(const NodePath& model, bool use_normals) : write_faces(odetrimeshdata_cat.debug()); +#ifdef dSINGLE if (!use_normals) { build_single(_vertices, sizeof(StridedVertex), _num_vertices, _faces, _num_faces * 3, sizeof(StridedTri)); @@ -111,6 +112,16 @@ OdeTriMeshData(const NodePath& model, bool use_normals) : _faces, _num_faces * 3, sizeof(StridedTri), _normals); } +#else + if (!use_normals) { + build_double(_vertices, sizeof(StridedVertex), _num_vertices, + _faces, _num_faces * 3, sizeof(StridedTri)); + } else { + build_double1(_vertices, sizeof(StridedVertex), _num_vertices, + _faces, _num_faces * 3, sizeof(StridedTri), + _normals); + } +#endif preprocess(); } From 5d9a5dbde18ac563d7da8e9b71f0925fcb83db96 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 5 Jan 2018 16:11:35 +0100 Subject: [PATCH 030/118] travis: add fakeroot package --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c8ca00ff11..ba9f3d059a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,4 +26,5 @@ addons: - python-dev - python3-dev - zlib1g-dev + - fakeroot script: $PYTHONV makepanda/makepanda.py --everything --git-commit $TRAVIS_COMMIT --installer --threads 4 From bdb53b8cb56b7251d61cfa19dbe3814dce412c72 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 5 Jan 2018 19:49:02 +0100 Subject: [PATCH 031/118] showbase: fixes for aspect-ratio adjustment of pixel2d(p) --- direct/src/showbase/ShowBase.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 26685a0693..580f2b6ce7 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -2732,9 +2732,10 @@ class ShowBase(DirectObject.DirectObject): # changed and update the camera lenses and aspect2d parameters self.adjustWindowAspectRatio(self.getAspectRatio()) - if win.getSideBySideStereo() and win.hasSize() and win.getSbsLeftYSize() != 0: + if win.hasSize() and win.getSbsLeftYSize() != 0: self.pixel2d.setScale(2.0 / win.getSbsLeftXSize(), 1.0, 2.0 / win.getSbsLeftYSize()) - self.pixel2dp.setScale(2.0 / win.getSbsLeftXSize(), 1.0, 2.0 / win.getSbsLeftYSize()) + if self.wantRender2dp: + self.pixel2dp.setScale(2.0 / win.getSbsLeftXSize(), 1.0, 2.0 / win.getSbsLeftYSize()) else: xsize, ysize = self.getSize() if xsize > 0 and ysize > 0: From b5e9f23e3b4cc73e953e91bf36976c9e1cc69fa8 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 7 Jan 2018 12:07:49 +0100 Subject: [PATCH 032/118] glesgsg: fix compile error on Arch Linux [skip ci] --- panda/src/glesgsg/glesgsg.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/panda/src/glesgsg/glesgsg.h b/panda/src/glesgsg/glesgsg.h index c0afb58b56..64d64db216 100644 --- a/panda/src/glesgsg/glesgsg.h +++ b/panda/src/glesgsg/glesgsg.h @@ -62,6 +62,9 @@ // #include #endif +// Some implementations (Arch Linux) set this in glext.h +typedef char GLchar; + #include "panda_esglext.h" // This helps to keep the source clean of hundreds of ifdefs. From 5831a3150976b316c8c6ba5987b16821f12d7f76 Mon Sep 17 00:00:00 2001 From: tobspr Date: Sun, 7 Jan 2018 22:21:28 +0100 Subject: [PATCH 033/118] Add RenderPipeline C++ lighting modules to Panda3D codebase --- contrib/src/rplight/config_rplight.cxx | 51 +++ contrib/src/rplight/config_rplight.h | 40 ++ contrib/src/rplight/gpuCommand.I | 185 ++++++++ contrib/src/rplight/gpuCommand.cxx | 87 ++++ contrib/src/rplight/gpuCommand.h | 89 ++++ contrib/src/rplight/gpuCommandList.cxx | 82 ++++ contrib/src/rplight/gpuCommandList.h | 54 +++ contrib/src/rplight/iesDataset.cxx | 233 ++++++++++ contrib/src/rplight/iesDataset.h | 68 +++ contrib/src/rplight/internalLightManager.I | 140 ++++++ contrib/src/rplight/internalLightManager.cxx | 441 +++++++++++++++++++ contrib/src/rplight/internalLightManager.h | 102 +++++ contrib/src/rplight/p3rplight_composite1.cxx | 13 + contrib/src/rplight/pointerSlotStorage.h | 228 ++++++++++ contrib/src/rplight/pssmCameraRig.I | 243 ++++++++++ contrib/src/rplight/pssmCameraRig.cxx | 396 +++++++++++++++++ contrib/src/rplight/pssmCameraRig.h | 125 ++++++ contrib/src/rplight/rpLight.I | 406 +++++++++++++++++ contrib/src/rplight/rpLight.cxx | 137 ++++++ contrib/src/rplight/rpLight.h | 130 ++++++ contrib/src/rplight/rpPointLight.I | 82 ++++ contrib/src/rplight/rpPointLight.cxx | 90 ++++ contrib/src/rplight/rpPointLight.h | 63 +++ contrib/src/rplight/rpSpotLight.I | 74 ++++ contrib/src/rplight/rpSpotLight.cxx | 80 ++++ contrib/src/rplight/rpSpotLight.h | 71 +++ contrib/src/rplight/shadowAtlas.I | 159 +++++++ contrib/src/rplight/shadowAtlas.cxx | 186 ++++++++ contrib/src/rplight/shadowAtlas.h | 80 ++++ contrib/src/rplight/shadowManager.I | 192 ++++++++ contrib/src/rplight/shadowManager.cxx | 157 +++++++ contrib/src/rplight/shadowManager.h | 91 ++++ contrib/src/rplight/shadowSource.I | 262 +++++++++++ contrib/src/rplight/shadowSource.cxx | 41 ++ contrib/src/rplight/shadowSource.h | 93 ++++ contrib/src/rplight/tagStateManager.I | 93 ++++ contrib/src/rplight/tagStateManager.cxx | 202 +++++++++ contrib/src/rplight/tagStateManager.h | 93 ++++ makepanda/makepanda.py | 23 + 39 files changed, 5382 insertions(+) create mode 100644 contrib/src/rplight/config_rplight.cxx create mode 100644 contrib/src/rplight/config_rplight.h create mode 100644 contrib/src/rplight/gpuCommand.I create mode 100644 contrib/src/rplight/gpuCommand.cxx create mode 100644 contrib/src/rplight/gpuCommand.h create mode 100644 contrib/src/rplight/gpuCommandList.cxx create mode 100644 contrib/src/rplight/gpuCommandList.h create mode 100644 contrib/src/rplight/iesDataset.cxx create mode 100644 contrib/src/rplight/iesDataset.h create mode 100644 contrib/src/rplight/internalLightManager.I create mode 100644 contrib/src/rplight/internalLightManager.cxx create mode 100644 contrib/src/rplight/internalLightManager.h create mode 100644 contrib/src/rplight/p3rplight_composite1.cxx create mode 100644 contrib/src/rplight/pointerSlotStorage.h create mode 100644 contrib/src/rplight/pssmCameraRig.I create mode 100644 contrib/src/rplight/pssmCameraRig.cxx create mode 100644 contrib/src/rplight/pssmCameraRig.h create mode 100644 contrib/src/rplight/rpLight.I create mode 100644 contrib/src/rplight/rpLight.cxx create mode 100644 contrib/src/rplight/rpLight.h create mode 100644 contrib/src/rplight/rpPointLight.I create mode 100644 contrib/src/rplight/rpPointLight.cxx create mode 100644 contrib/src/rplight/rpPointLight.h create mode 100644 contrib/src/rplight/rpSpotLight.I create mode 100644 contrib/src/rplight/rpSpotLight.cxx create mode 100644 contrib/src/rplight/rpSpotLight.h create mode 100644 contrib/src/rplight/shadowAtlas.I create mode 100644 contrib/src/rplight/shadowAtlas.cxx create mode 100644 contrib/src/rplight/shadowAtlas.h create mode 100644 contrib/src/rplight/shadowManager.I create mode 100644 contrib/src/rplight/shadowManager.cxx create mode 100644 contrib/src/rplight/shadowManager.h create mode 100644 contrib/src/rplight/shadowSource.I create mode 100644 contrib/src/rplight/shadowSource.cxx create mode 100644 contrib/src/rplight/shadowSource.h create mode 100644 contrib/src/rplight/tagStateManager.I create mode 100644 contrib/src/rplight/tagStateManager.cxx create mode 100644 contrib/src/rplight/tagStateManager.h diff --git a/contrib/src/rplight/config_rplight.cxx b/contrib/src/rplight/config_rplight.cxx new file mode 100644 index 0000000000..563c4db641 --- /dev/null +++ b/contrib/src/rplight/config_rplight.cxx @@ -0,0 +1,51 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "config_rplight.h" + +#include "rpLight.h" +#include "rpPointLight.h" + +#include "dconfig.h" + +Configure(config_rplight); +NotifyCategoryDef(rplight, ""); + +ConfigureFn(config_rplight) { + init_librplight(); +} + +void +init_librplight() { + static bool initialized = false; + if (initialized) { + return; + } + initialized = true; + + // RPLight::init_type(); + // RPPointLight::init_type(); +} diff --git a/contrib/src/rplight/config_rplight.h b/contrib/src/rplight/config_rplight.h new file mode 100644 index 0000000000..10cf63fc56 --- /dev/null +++ b/contrib/src/rplight/config_rplight.h @@ -0,0 +1,40 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef CONFIG_RPLIGHT_H +#define CONFIG_RPLIGHT_H + +#include "pandabase.h" +#include "notifyCategoryProxy.h" +#include "configVariableDouble.h" +#include "configVariableString.h" +#include "configVariableInt.h" + +NotifyCategoryDecl(rplight, EXPORT_CLASS, EXPORT_TEMPL); + +extern void init_librplight(); + +#endif // CONFIG_RPLIGHT_H diff --git a/contrib/src/rplight/gpuCommand.I b/contrib/src/rplight/gpuCommand.I new file mode 100644 index 0000000000..3531eed2ba --- /dev/null +++ b/contrib/src/rplight/gpuCommand.I @@ -0,0 +1,185 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "stdint.h" + +/** + * @brief Appends an integer to the GPUCommand. + * @details This adds an integer to the back of the GPUCommand. Depending on the + * setting in convert_int_to_float, this will either just convert the int to a + * float by casting it, or just do a bitwise copy. + * + * @param v The integer to append. + */ +inline void GPUCommand::push_int(int v) { + push_float(convert_int_to_float(v)); +} + +/** + * @brief Internal method to convert an integer to float + * @details This methods gets called by the GPUCommand::push_int, and manages + * storing an integer in a floating point variable. There are two options, + * which are documented inside of the method. + * + * @param v Integer to convert + * @return Float-representation of that integer, either casted or binary converted.s + */ +inline float GPUCommand::convert_int_to_float(int v) const { + + #if !PACK_INT_AS_FLOAT + // Just round to float, can cause rounding issues tho + return (float)v; + + #else + assert(sizeof(float) == 4); // We really need this for packing! Better + // throw an error if the compiler uses more + // than 4 bytes. + // Simple binary conversion, assuming sizeof(int) == sizeof(float) + union { int32_t _int; float _float; } converter = { (int32_t)v }; + return converter._float; + #endif +} + +/** + * @brief Appends a float to the GPUCommand. + * @details This adds an integer to the back of the GPUCommand. Its used by all + * other push_xxx methods, and simply stores the value, then increments the write + * pointer. When the amount of floats exceeds the capacity of the GPUCommand, + * an error will be printed, and the method returns without doing anything else. + * + * @param v The float to append. + */ +inline void GPUCommand::push_float(float v) { + if (_current_index >= GPU_COMMAND_ENTRIES) { + gpucommand_cat.error() << "Out of bounds! Exceeded command size of " << GPU_COMMAND_ENTRIES << endl; + return; + } + _data[_current_index++] = v; +} + +/** + * @brief Appends a 3-component floating point vector to the GPUCommand. + * @details This appends a 3-component floating point vector to the command. + * It basically just calls push_float() for every component, in the order + * x, y, z, which causes the vector to occupy the space of 3 floats. + * + * @param v Int-Vector to append. + */ +inline void GPUCommand::push_vec3(const LVecBase3 &v) { + push_float(v.get_x()); + push_float(v.get_y()); + push_float(v.get_z()); +} + + +/** + * @brief Appends a 3-component integer vector to the GPUCommand. + * @details This appends a 3-component integer vector to the command. + * It basically just calls push_int() for every component, in the order + * x, y, z, which causes the vector to occupy the space of 3 floats. + * + * @param v Int-Vector to append. + */ +inline void GPUCommand::push_vec3(const LVecBase3i &v) { + push_int(v.get_x()); + push_int(v.get_y()); + push_int(v.get_z()); +} + +/** + * @brief Appends a 4-component floating point vector to the GPUCommand. + * @details This appends a 4-component floating point vector to the command. + * It basically just calls push_float() for every component, in the order + * x, y, z, which causes the vector to occupy the space of 3 floats. + * + * @param v Int-Vector to append. + */ +inline void GPUCommand::push_vec4(const LVecBase4 &v) { + push_float(v.get_x()); + push_float(v.get_y()); + push_float(v.get_z()); + push_float(v.get_w()); +} + +/** + * @brief Appends a 4-component integer vector to the GPUCommand. + * @details This appends a 4-component integer vector to the command. + * It basically just calls push_int() for every component, in the order + * x, y, z, w, which causes the vector to occupy the space of 4 floats. + * + * @param v Int-Vector to append. + */ +inline void GPUCommand::push_vec4(const LVecBase4i &v) { + push_int(v.get_x()); + push_int(v.get_y()); + push_int(v.get_z()); + push_int(v.get_w()); +} + +/** + * @brief Appends a floating point 3x3 matrix to the GPUCommand. + * @details This appends a floating point 3x3 matrix to the GPUCommand, by + * pushing all components in row-order to the command. This occupies a space of + * 9 floats. + * + * @param v Matrix to append + */ +inline void GPUCommand::push_mat3(const LMatrix3 &v) { + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + push_float(v.get_cell(i, j)); + } + } +} + +/** + * @brief Appends a floating point 4x4 matrix to the GPUCommand. + * @details This appends a floating point 4x4 matrix to the GPUCommand, by + * pushing all components in row-order to the command. This occupies a space of + * 16 floats. + * + * @param v Matrix to append + */ +inline void GPUCommand::push_mat4(const LMatrix4 &v) { + for (size_t i = 0; i < 4; ++i) { + for (size_t j = 0; j < 4; ++j) { + push_float(v.get_cell(i, j)); + } + } +} + +/** + * @brief Returns whether integers are packed as floats. + * @details This returns how integer are packed into the data stream. If the + * returned value is true, then integers are packed using their binary + * representation converted to floating point format. If the returned value + * is false, then integers are packed by simply casting them to float, + * e.g. val = (float)i; + * @return The integer representation flag + */ +inline bool GPUCommand::get_uses_integer_packing() { + return PACK_INT_AS_FLOAT; +} diff --git a/contrib/src/rplight/gpuCommand.cxx b/contrib/src/rplight/gpuCommand.cxx new file mode 100644 index 0000000000..2bd468c902 --- /dev/null +++ b/contrib/src/rplight/gpuCommand.cxx @@ -0,0 +1,87 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "gpuCommand.h" + +#include +#include +#include + + +NotifyCategoryDef(gpucommand, ""); + +/** + * @brief Constructs a new GPUCommand with the given command type. + * @details This will construct a new GPUCommand of the given command type. + * The command type should be of GPUCommand::CommandType, and determines + * what data the GPUCommand contains, and how it will be handled. + * + * @param command_type The type of the GPUCommand + */ +GPUCommand::GPUCommand(CommandType command_type) { + _command_type = command_type; + _current_index = 0; + memset(_data, 0x0, sizeof(float) * GPU_COMMAND_ENTRIES); + + // Store the command type as the first entry + push_int(command_type); +} + +/** + * @brief Prints out the GPUCommand to the console + * @details This method prints the type, size, and data of the GPUCommand to the + * console. This helps for debugging the contents of the GPUCommand. Keep + * in mind that integers might be shown in their binary float representation, + * depending on the setting in the GPUCommand::convert_int_to_float method. + */ +void GPUCommand::write(ostream &out) const { + out << "GPUCommand(type=" << _command_type << ", size=" << _current_index << ", data = {" << endl; + for (size_t k = 0; k < GPU_COMMAND_ENTRIES; ++k) { + out << std::setw(12) << std::fixed << std::setprecision(5) << _data[k] << " "; + if (k % 6 == 5 || k == GPU_COMMAND_ENTRIES - 1) out << endl; + } + out << "})" << endl; +} + +/** + * @brief Writes the GPU command to a given target. + * @details This method writes all the data of the GPU command to a given target. + * The target should be a pointer to memory being big enough to hold the + * data. Presumably #dest will be a handle to texture memory. + * The command_index controls the offset where the data will be written + * to. + * + * @param dest Handle to the memory to write the command to + * @param command_index Offset to write the command to. The command will write + * its data to command_index * GPU_COMMAND_ENTRIES. When writing + * the GPUCommand in a GPUCommandList, the command_index will + * most likely be the index of the command in the list. + */ +void GPUCommand::write_to(const PTA_uchar &dest, size_t command_index) { + size_t command_size = GPU_COMMAND_ENTRIES * sizeof(float); + size_t offset = command_index * command_size; + memcpy(dest.p() + offset, &_data, command_size); +} diff --git a/contrib/src/rplight/gpuCommand.h b/contrib/src/rplight/gpuCommand.h new file mode 100644 index 0000000000..7fa784b975 --- /dev/null +++ b/contrib/src/rplight/gpuCommand.h @@ -0,0 +1,89 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef GPUCOMMAND_H +#define GPUCOMMAND_H + +#include "pandabase.h" +#include "luse.h" + +NotifyCategoryDecl(gpucommand, EXPORT_CLASS, EXPORT_TEMPL); + +#define GPU_COMMAND_ENTRIES 32 + +// Packs integers by storing their binary representation in floats +// This only works if the command and light buffer is 32bit floating point. +#define PACK_INT_AS_FLOAT 0 + +/** + * @brief Class for storing data to be transferred to the GPU. + * @details This class can be seen like a packet, to be transferred to the GPU. + * It has a command type, which tells the GPU what to do once it recieved this + * "packet". It stores a limited amount of floating point components. + */ +class GPUCommand { +PUBLISHED: + /** + * The different types of GPUCommands. Each type has a special case in + * the command queue processor. When adding new types, those need to + * be handled in the command target, too. + */ + enum CommandType { + CMD_invalid = 0, + CMD_store_light = 1, + CMD_remove_light = 2, + CMD_store_source = 3, + CMD_remove_sources = 4, + }; + + GPUCommand(CommandType command_type); + + inline void push_int(int v); + inline void push_float(float v); + inline void push_vec3(const LVecBase3 &v); + inline void push_vec3(const LVecBase3i &v); + inline void push_vec4(const LVecBase4 &v); + inline void push_vec4(const LVecBase4i &v); + inline void push_mat3(const LMatrix3 &v); + inline void push_mat4(const LMatrix4 &v); + + inline static bool get_uses_integer_packing(); + + void write_to(const PTA_uchar &dest, size_t command_index); + void write(ostream &out) const; + +private: + + inline float convert_int_to_float(int v) const; + + CommandType _command_type; + size_t _current_index; + float _data[GPU_COMMAND_ENTRIES]; +}; + +#include "gpuCommand.I" + +#endif // GPUCOMMAND_H diff --git a/contrib/src/rplight/gpuCommandList.cxx b/contrib/src/rplight/gpuCommandList.cxx new file mode 100644 index 0000000000..3f92194f64 --- /dev/null +++ b/contrib/src/rplight/gpuCommandList.cxx @@ -0,0 +1,82 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "gpuCommandList.h" + + +/** + * @brief Constructs a new GPUCommandList + * @details This constructs a new GPUCommandList. By default, there are no commands + * in the list. + */ +GPUCommandList::GPUCommandList() { +} + +/** + * @brief Pushes a GPUCommand to the command list. + * @details This adds a new GPUCommand to the list of commands to be processed. + * + * @param cmd The command to add + */ +void GPUCommandList::add_command(const GPUCommand& cmd) { + _commands.push(cmd); +} + +/** + * @brief Returns the number of commands in this list. + * @details This returns the amount of commands which are currently stored in this + * list, and are waiting to get processed. + * @return Amount of commands + */ +size_t GPUCommandList::get_num_commands() { + return _commands.size(); +} + +/** + * @brief Writes the first n-commands to a destination. + * @details This takes the first #limit commands, and writes them to the + * destination using GPUCommand::write_to. See GPUCommand::write_to for + * further information about #dest. The limit controls after how much + * commands the processing will be stopped. All commands which got processed + * will get removed from the list. + * + * @param dest Destination to write to, see GPUCommand::write_to + * @param limit Maximum amount of commands to process + * + * @return Amount of commands processed, between 0 and #limit. + */ +size_t GPUCommandList::write_commands_to(const PTA_uchar &dest, size_t limit) { + size_t num_commands_written = 0; + + while (num_commands_written < limit && !_commands.empty()) { + // Write the first command to the stream, and delete it afterwards + _commands.front().write_to(dest, num_commands_written); + _commands.pop(); + num_commands_written ++; + } + + return num_commands_written; +} diff --git a/contrib/src/rplight/gpuCommandList.h b/contrib/src/rplight/gpuCommandList.h new file mode 100644 index 0000000000..f2ec280116 --- /dev/null +++ b/contrib/src/rplight/gpuCommandList.h @@ -0,0 +1,54 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef GPUCOMMANDLIST_H +#define GPUCOMMANDLIST_H + +#include "pandabase.h" +#include "gpuCommand.h" + +#include + +/** + * @brief Class to store a list of commands. + * @details This is a class to store a list of GPUCommands. It provides + * functionality to only provide the a given amount of commands at one time. + */ +class GPUCommandList { +PUBLISHED: + GPUCommandList(); + + void add_command(const GPUCommand& cmd); + size_t get_num_commands(); + size_t write_commands_to(const PTA_uchar &dest, size_t limit = 32); + + MAKE_PROPERTY(num_commands, get_num_commands); + +protected: + queue _commands; +}; + +#endif // GPUCOMMANDLIST_H diff --git a/contrib/src/rplight/iesDataset.cxx b/contrib/src/rplight/iesDataset.cxx new file mode 100644 index 0000000000..2975268138 --- /dev/null +++ b/contrib/src/rplight/iesDataset.cxx @@ -0,0 +1,233 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "iesDataset.h" + +#define _USE_MATH_DEFINES +#include + +NotifyCategoryDef(iesdataset, "") + +/** + * @brief Constructs a new empty dataset. + * @details This constructs a new IESDataset with no data set. + */ +IESDataset::IESDataset() { +} + +/** + * @brief Sets the vertical angles of the dataset. + * @details This sets the list of vertical angles of the dataset. + * + * @param vertical_angles Vector of all vertical angles. + */ +void IESDataset::set_vertical_angles(const PTA_float &vertical_angles) { + nassertv(vertical_angles.size() > 0); + _vertical_angles = vertical_angles; +} + +/** + * @brief Sets the horizontal angles of the dataset. + * @details This sets the list of horizontal angles of the dataset. + * + * @param horizontal_angles Vector of all horizontal angles. + */ +void IESDataset::set_horizontal_angles(const PTA_float &horizontal_angles) { + nassertv(horizontal_angles.size() > 0); + _horizontal_angles = horizontal_angles; +} + +/** + * @brief Sets the candela values. + * @details This sets the candela values of the dataset. They should be an + * interleaved 2D array with the dimensions vertical_angles x horizontal_angles. + * They also should be normalized by dividing by the maximum entry. + * @param candela_values Interleaved 2D-vector of candela values. + */ +void IESDataset::set_candela_values(const PTA_float &candela_values) { + nassertv(candela_values.size() == _horizontal_angles.size() * _vertical_angles.size()); + _candela_values = candela_values; +} + +/** + * @brief Internal method to access the candela data. + * @details This lookups a candela value in the candela values. It converts a + * two dimensional index to a onedimensional index and then returns the candela + * value at that position. + * + * @param vertical_angle_idx Index of the vertical angle + * @param horizontal_angle_idx Index of the horizontal angle + * + * @return Candela value between 0 .. 1 + */ +float IESDataset::get_candela_value_from_index(size_t vertical_angle_idx, size_t horizontal_angle_idx) const { + size_t index = vertical_angle_idx + horizontal_angle_idx * _vertical_angles.size(); + nassertr(index >= 0 && index < _candela_values.size(), 0.0); + return _candela_values[index]; +} + +/** + * @brief Samples the dataset at the given position + * @details This looks up a value in the dataset, by specifying a horizontal and + * vertical angle. This is used for generating the LUT. The vertical and horizontal + * angle should be inside of the bounds of the vertical and horizontal angle arrays. + * + * @param vertical_angle Vertical angle, from 0 .. 90 or 0 .. 180 depending on the dataset + * @param horizontal_angle Horizontal angle, from 0 .. 180 or 0 .. 360 depending on the dataset. + * + * @return Candela value between 0 .. 1 + */ +float IESDataset::get_candela_value(float vertical_angle, float horizontal_angle) const { + + // Special case for datasets without horizontal angles + if (_horizontal_angles.size() == 1) { + return get_vertical_candela_value(0, vertical_angle); + } + + float max_angle = _horizontal_angles[_horizontal_angles.size() - 1]; + + // Wrap angle to fit from 0 .. 360 degree. Most profiles only distribute + // candela values from 0 .. 180 or even 0 .. 90. We have to mirror the + // values at those borders (so 2 times for 180 degree and 4 times for 90 degree) + horizontal_angle = fmod(horizontal_angle, 2.0f * max_angle); + if (horizontal_angle > max_angle) { + horizontal_angle = 2.0 * max_angle - horizontal_angle; + } + + // Simlar to the vertical step, we now try interpolating a horizontal angle, + // but we need to evaluate the vertical value for each row instead of fetching + // the value directly + for (size_t horizontal_index = 1; horizontal_index < _horizontal_angles.size(); ++horizontal_index) { + float curr_angle = _horizontal_angles[horizontal_index]; + + if (curr_angle >= horizontal_angle) { + + // Get previous angle data + float prev_angle = _horizontal_angles[horizontal_index - 1]; + float prev_value = get_vertical_candela_value(horizontal_index - 1, vertical_angle); + float curr_value = get_vertical_candela_value(horizontal_index, vertical_angle); + + // Interpolate lineary + float lerp = (horizontal_angle - prev_angle) / (curr_angle - prev_angle); + + // Should never occur, but to be safe: + if (lerp < 0.0 || lerp > 1.0) { + iesdataset_cat.error() << "Invalid horizontal lerp: " << lerp + << ", requested angle was " << horizontal_angle + << ", prev = " << prev_angle << ", cur = " << curr_angle + << endl; + } + + return curr_value * lerp + prev_value * (1-lerp); + } + } + + return 0.0; +} + +/** + * @brief Fetches a vertical candela value + * @details Fetches a vertical candela value, using a given horizontal position. + * This does an 1D interpolation in the candela values array. + * + * @param horizontal_angle_idx The index of the horizontal angle in the horizontal + * angle array. + * @param vertical_angle The vertical angle. Interpolation will be done if the + * vertical angle is not in the vertical angles array. + * + * @return Candela value between 0 .. 1 + */ +float IESDataset::get_vertical_candela_value(size_t horizontal_angle_idx, float vertical_angle) const { + nassertr(horizontal_angle_idx >= 0 && horizontal_angle_idx < _horizontal_angles.size(), 0.0); + + // Lower bound + if (vertical_angle < 0.0) return 0.0; + + // Upper bound + if (vertical_angle > _vertical_angles[_vertical_angles.size() - 1] ) return 0.0; + + // Find lowest enclosing angle + for (size_t vertical_index = 1; vertical_index < _vertical_angles.size(); ++vertical_index) { + float curr_angle = _vertical_angles[vertical_index]; + + // Found value + if (curr_angle > vertical_angle) { + + // Get previous angle data + float prev_angle = _vertical_angles[vertical_index - 1]; + float prev_value = get_candela_value_from_index(vertical_index - 1, horizontal_angle_idx); + float curr_value = get_candela_value_from_index(vertical_index, horizontal_angle_idx); + + // Interpolate lineary + float lerp = (vertical_angle - prev_angle) / (curr_angle - prev_angle); + + // Should never occur, but to be safe: + if (lerp < 0.0 || lerp > 1.0) { + iesdataset_cat.error() << "ERROR: Invalid vertical lerp: " << lerp + << ", requested angle was " << vertical_angle + << ", prev = " << prev_angle << ", cur = " << curr_angle + << endl; + } + + return curr_value * lerp + prev_value * (1-lerp); + } + } + return 0.0; +} + +/** + * @brief Generates the IES LUT + * @details This generates the LUT into a given dataset texture. The x-axis + * referes to the vertical_angle, whereas the y-axis refers to the + * horizontal angle. + * + * @param dest_tex Texture to write the LUT into + * @param z Layer to write the LUT into, in case the texture is a 3D Texture or + * 2D Texture Array. + */ +void IESDataset::generate_dataset_texture_into(Texture* dest_tex, size_t z) const { + + size_t resolution_vertical = dest_tex->get_y_size(); + size_t resolution_horizontal = dest_tex->get_x_size(); + + // Candla values are stored flippped - vertical angles in the x - Axis + // and horizontal angles in the y - Axis + PNMImage dest = PNMImage(resolution_vertical, resolution_horizontal, 1, 65535); + + for (size_t vert = 0; vert < resolution_vertical; ++vert) { + for (size_t horiz = 0; horiz < resolution_horizontal; ++horiz) { + float vert_angle = (float)vert / (float)(resolution_vertical-1); + vert_angle = cos(vert_angle * M_PI) * 90.0 + 90.0; + float horiz_angle = (float)horiz / (float)(resolution_horizontal-1) * 360.0; + float candela = get_candela_value(vert_angle, horiz_angle); + dest.set_xel(vert, horiz, candela); + } + } + + + dest_tex->load(dest, z, 0); +} diff --git a/contrib/src/rplight/iesDataset.h b/contrib/src/rplight/iesDataset.h new file mode 100644 index 0000000000..3f7ee09e7e --- /dev/null +++ b/contrib/src/rplight/iesDataset.h @@ -0,0 +1,68 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef IESDATASET_H +#define IESDATASET_H + +#include "pandabase.h" +#include "pta_float.h" +#include "pointerToArray.h" +#include "texture.h" +#include "pnmImage.h" + +NotifyCategoryDecl(iesdataset, EXPORT_CLASS, EXPORT_TEMPL); + + +/** + * @brief This class generates a LUT from IES data. + * @details This class is used by the IESLoader to generate a LUT texture which + * is used in the shaders to perform IES lighting. It takes a set of vertical + * and horizontal angles, as well as a set of candela values, which then are + * lineary interpolated onto a 2D LUT Texture. + */ +class IESDataset { +PUBLISHED: + IESDataset(); + + void set_vertical_angles(const PTA_float &vertical_angles); + void set_horizontal_angles(const PTA_float &horizontal_angles); + void set_candela_values(const PTA_float &candela_values); + + void generate_dataset_texture_into(Texture* dest_tex, size_t z) const; + +public: + + float get_candela_value(float vertical_angle, float horizontal_angle) const; + float get_candela_value_from_index(size_t vertical_angle_idx, size_t horizontal_angle_idx) const; + float get_vertical_candela_value(size_t horizontal_angle_idx, float vertical_angle) const; + +private: + PTA_float _vertical_angles; + PTA_float _horizontal_angles; + PTA_float _candela_values; +}; + +#endif // IESDATASET_H diff --git a/contrib/src/rplight/internalLightManager.I b/contrib/src/rplight/internalLightManager.I new file mode 100644 index 0000000000..25e27e5d44 --- /dev/null +++ b/contrib/src/rplight/internalLightManager.I @@ -0,0 +1,140 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +/** + * @brief Returns the maximum light index + * @details This returns the maximum light index (also called slot). Any lights + * after that slot are guaranteed to be zero-lights. This is useful when + * iterating over the list of lights, because iteration can be stopped when + * the maximum light index is reached. + * + * The maximum light index points to the last slot which is used. If no lights + * are attached, -1 is returned. If one light is attached at slot 0, the index + * is 0, if two are attached at the slots 0 and 1, the index is 1, and so on. + * + * If, for example, two lights are attached at the slots 2 and 5, then the + * index will be 5. Keep in mind that the max-index is not an indicator for + * how many lights are attached. Also, zero lights still may occur when iterating + * over the light lists + * + * @return Maximum light index + */ +inline int InternalLightManager::get_max_light_index() const { + return _lights.get_max_index(); +} + +/** + * @brief Returns the amount of stored lights. + * @details This returns the amount of stored lights. This behaves unlike + * InternalLightManager::get_max_light_index, and instead returns the true + * amount of lights, which is completely unrelated to the amount of used slots. + * + * @return Amount of stored lights + */ +inline size_t InternalLightManager::get_num_lights() const { + return _lights.get_num_entries(); +} + +/** + * @brief Returns the amount of shadow sources. + * @details This returns the total amount of stored shadow sources. This does + * not denote the amount of updated sources, but instead takes into account + * all sources, even those out of frustum. + * @return Amount of shadow sources. + */ +inline size_t InternalLightManager::get_num_shadow_sources() const { + return _shadow_sources.get_num_entries(); +} + +/** + * @brief Sets the handle to the shadow manager + * @details This sets the handle to the global shadow manager. It is usually + * constructed on the python side, so we need to get a handle to it. + * + * The manager should be a handle to a ShadowManager instance, and will be + * stored somewhere on the python side most likely. The light manager does not + * keep a reference to it, so the python side should make sure to keep one. + * + * Be sure to call this before the InternalLightManager::update() method is + * called, otherwise an assertion will get triggered. + * + * @param mgr The ShadowManager instance + */ +inline void InternalLightManager::set_shadow_manager(ShadowManager* mgr) { + _shadow_manager = mgr; +} + +/** + * @brief Sets a handle to the command list + * @details This sets a handle to the global GPUCommandList. This is required to + * emit GPUCommands, which are used for attaching and detaching lights, as well + * as shadow source updates. + * + * The cmd_list should be a handle to a GPUCommandList handle, and will be + * stored somewhere on the python side most likely. The light manager does not + * keep a reference to it, so the python side should make sure to keep one. + * + * Be sure to call this before the InternalLightManager::update() method is + * called, otherwise an assertion will get triggered. + * + * @param cmd_list The GPUCommandList instance + */ +inline void InternalLightManager::set_command_list(GPUCommandList *cmd_list) { + _cmd_list = cmd_list; +} + +/** + * @brief Sets the camera position + * @details This sets the camera position, which will be used to determine which + * shadow sources have to get updated + * + * @param mat View projection mat + */ +inline void InternalLightManager::set_camera_pos(const LPoint3 &pos) { + _camera_pos = pos; +} + +/** + * @brief Sets the maximum shadow update distance + * @details This controls the maximum distance until which shadows are updated. + * If a shadow source is past that distance, it is ignored and no longer recieves + * updates until it is in range again + * + * @param dist Distance in world space units + */ +inline void InternalLightManager::set_shadow_update_distance(PN_stdfloat dist) { + _shadow_update_distance = dist; +} + +/** + * @brief Returns the internal used ShadowManager + * @details This returns a handle to the internally used shadow manager + * @return Shadow manager + */ +inline ShadowManager* InternalLightManager::get_shadow_manager() const { + return _shadow_manager; +} diff --git a/contrib/src/rplight/internalLightManager.cxx b/contrib/src/rplight/internalLightManager.cxx new file mode 100644 index 0000000000..ea45c518fd --- /dev/null +++ b/contrib/src/rplight/internalLightManager.cxx @@ -0,0 +1,441 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "internalLightManager.h" + +#include + +NotifyCategoryDef(lightmgr, ""); + + +/** + * @brief Constructs the light manager + * @details This constructs the light manager, initializing the light and shadow + * storage. You should set a command list and shadow manager before calling + * InternalLightManager::update. s + */ +InternalLightManager::InternalLightManager() { + _shadow_update_distance = 100.0; + _cmd_list = nullptr; + _shadow_manager = nullptr; +} + +/** + * @brief Adds a new light. + * @details This adds a new light to the list of lights. This will throw an + * error and return if the light is already attached. You may only call + * this after the ShadowManager was already set. + * + * While the light is attached, the light manager keeps a reference to it, so + * the light does not get destructed. + * + * This also setups the shadows on the light, in case shadows are enabled. + * While a light is attached, you can not change whether it casts shadows or not. + * To do so, detach the light, change the setting, and re-add the light. + * + * In case no free light slot is available, an error will be printed and no + * action will be performed. + * + * If no shadow manager was set, an assertion will be triggered. + * + * @param light The light to add. + */ +void InternalLightManager::add_light(PT(RPLight) light) { + nassertv(_shadow_manager != nullptr); // Shadow manager not set yet! + + // Don't attach the light in case its already attached + if (light->has_slot()) { + lightmgr_cat.error() << "could not add light because it already is attached! " + << "Detach the light first, then try it again." << endl; + return; + } + + // Find a free slot + size_t slot; + if (!_lights.find_slot(slot)) { + lightmgr_cat.error() << "Light limit of " << MAX_LIGHT_COUNT << " reached, " + << "all light slots used!" << endl; + return; + } + + // Reference the light because we store it, to avoid it getting destructed + // on the python side while we still work with it. The reference will be + // removed when the light gets detached. + light->ref(); + + // Reserve the slot + light->assign_slot(slot); + _lights.reserve_slot(slot, light); + + // Setup the shadows in case the light uses them + if (light->get_casts_shadows()) { + setup_shadows(light); + } + + // Store the light on the gpu, to make sure the GPU directly knows about it. + // We could wait until the next update cycle, but then we might be one frame + // too late already. + gpu_update_light(light); +} + +/** + * @brief Internal method to setup shadows for a light + * @details This method gets called by the InternalLightManager::add_light method + * to setup a lights shadow sources, in case shadows are enabled on that light. + * + * It finds a slot for all shadow sources of the ilhgt, and inits the shadow + * sources as well. If no slot could be found, an error is printed an nothing + * happens. + * + * @param light The light to init the shadow sources for + */ +void InternalLightManager::setup_shadows(RPLight* light) { + + // Init the lights shadow sources, and also call update once to make sure + // the sources are properly initialized + light->init_shadow_sources(); + light->update_shadow_sources(); + + // Find consecutive slots, this is important for PointLights so we can just + // store the first index of the source, and get the other slots by doing + // first_index + 1, +2 and so on. + size_t base_slot; + size_t num_sources = light->get_num_shadow_sources(); + if (!_shadow_sources.find_consecutive_slots(base_slot, num_sources)) { + lightmgr_cat.error() << "Failed to find slot for shadow sources! " + << "Shadow-Source limit of " << MAX_SHADOW_SOURCES + << " reached!" << endl; + return; + } + + // Init all sources + for (int i = 0; i < num_sources; ++i) { + ShadowSource* source = light->get_shadow_source(i); + + // Set the source as dirty, so it gets updated in the beginning + source->set_needs_update(true); + + // Assign the slot to the source. Since we got consecutive slots, we can + // just do base_slot + N. + size_t slot = base_slot + i; + _shadow_sources.reserve_slot(slot, source); + source->set_slot(slot); + } +} + +/** + * @brief Removes a light + * @details This detaches a light. This prevents it from being rendered, and also + * cleans up all resources used by that light. If no reference is kept on the + * python side, the light will also get destructed. + * + * If the light was not previously attached with InternalLightManager::add_light, + * an error will be triggered and nothing happens. + * + * In case the light was set to cast shadows, all shadow sources are cleaned + * up, and their regions in the shadow atlas are freed. + * + * All resources used by the light in the light and shadow storage are also + * cleaned up, by emitting cleanup GPUCommands. + * + * If no shadow manager was set, an assertion will be triggered. + * + * @param light [description] + */ +void InternalLightManager::remove_light(PT(RPLight) light) { + nassertv(_shadow_manager != nullptr); + + if (!light->has_slot()) { + lightmgr_cat.error() << "Could not detach light, light was not attached!" << endl; + return; + } + + // Free the lights slot in the light storage + _lights.free_slot(light->get_slot()); + + // Tell the GPU we no longer need the lights data + gpu_remove_light(light); + + // Mark the light as detached. After this call, we can not call get_slot + // anymore, so its important we do this after we unregistered the light + // from everywhere. + light->remove_slot(); + + // Clear shadow related stuff, in case the light casts shadows + if (light->get_casts_shadows()) { + + // Free the slots of all sources, and also unregister their regions from + // the shadow atlas. + for (size_t i = 0; i < light->get_num_shadow_sources(); ++i) { + ShadowSource* source = light->get_shadow_source(i); + if (source->has_slot()) { + _shadow_sources.free_slot(source->get_slot()); + } + if (source->has_region()) { + _shadow_manager->get_atlas()->free_region(source->get_region()); + source->clear_region(); + } + } + + // Remove all sources of the light by emitting a consecutive remove command + gpu_remove_consecutive_sources(light->get_shadow_source(0), + light->get_num_shadow_sources()); + + // Finally remove all shadow sources. This is important in case the light + // will be re-attached. Otherwise an assertion will get triggered. + light->clear_shadow_sources(); + } + + // Since we referenced the light when we stored it, we have to decrease + // the reference now. In case no reference was kept on the python side, + // the light will get destructed soon. + light->unref(); +} + +/** + * @brief Internal method to remove consecutive sources from the GPU. + * @details This emits a GPUCommand to consecutively remove shadow sources from + * the GPU. This is called when a light gets removed, to free the space its + * shadow sources took. Its not really required, because as long as the light + * is not used, there is no reference to the sources. However, it can't hurt to + * cleanup the memory. + * + * All sources starting at first_source->get_slot() until + * first_source->get_slot() + num_sources will get cleaned up. + * + * @param first_source First source of the light + * @param num_sources Amount of consecutive sources to clear + */ +void InternalLightManager::gpu_remove_consecutive_sources(ShadowSource *first_source, + size_t num_sources) { + nassertv(_cmd_list != nullptr); // No command list set yet + nassertv(first_source->has_slot()); // Source has no slot! + GPUCommand cmd_remove(GPUCommand::CMD_remove_sources); + cmd_remove.push_int(first_source->get_slot()); + cmd_remove.push_int(num_sources); + _cmd_list->add_command(cmd_remove); +} + +/** + * @brief Internal method to remove a light from the GPU. + * @details This emits a GPUCommand to clear a lights data. This sets the data + * to all zeros, marking that no light is stored anymore. + * + * This throws an assertion in case the light is not currently attached. Be + * sure to call this before detaching the light. + * + * @param light The light to remove, must be attached. + */ +void InternalLightManager::gpu_remove_light(RPLight* light) { + nassertv(_cmd_list != nullptr); // No command list set yet + nassertv(light->has_slot()); // Light has no slot! + GPUCommand cmd_remove(GPUCommand::CMD_remove_light); + cmd_remove.push_int(light->get_slot()); + _cmd_list->add_command(cmd_remove); +} + +/** + * @brief Updates a lights data on the GPU + * @details This method emits a GPUCommand to update a lights data. This can + * be used to initially store the lights data, or to update the data whenever + * the light changed. + * + * This throws an assertion in case the light is not currently attached. Be + * sure to call this after attaching the light. + * + * @param light The light to update + */ +void InternalLightManager::gpu_update_light(RPLight* light) { + nassertv(_cmd_list != nullptr); // No command list set yet + nassertv(light->has_slot()); // Light has no slot! + GPUCommand cmd_update(GPUCommand::CMD_store_light); + cmd_update.push_int(light->get_slot()); + light->write_to_command(cmd_update); + light->set_needs_update(false); + _cmd_list->add_command(cmd_update); +} + +/** + * @brief Updates a shadow source data on the GPU + * @details This emits a GPUCommand to update a given shadow source, storing all + * data of the source on the GPU. This can also be used to initially store a + * ShadowSource, since all data will be overridden. + * + * This throws an assertion if the source has no slot yet. + * + * @param source The source to update + */ +void InternalLightManager::gpu_update_source(ShadowSource* source) { + nassertv(_cmd_list != nullptr); // No command list set yet + nassertv(source->has_slot()); // Source has no slot! + GPUCommand cmd_update(GPUCommand::CMD_store_source); + cmd_update.push_int(source->get_slot()); + source->write_to_command(cmd_update); + _cmd_list->add_command(cmd_update); +} + +/** + * @brief Internal method to update all lights + * @details This is called by the main update method, and iterates over the list + * of lights. If a light is marked as dirty, it will recieve an update of its + * data and its shadow sources. + */ +void InternalLightManager::update_lights() { + for (auto iter = _lights.begin(); iter != _lights.end(); ++iter) { + RPLight* light = *iter; + if (light && light->get_needs_update()) { + if (light->get_casts_shadows()) { + light->update_shadow_sources(); + } + gpu_update_light(light); + } + } +} + +/** + * @brief Compares shadow sources by their priority + * @details Returns if a has a greater priority than b. This depends on the + * resolution of the source, and also if the source has a region or not. + * This method can be passed to std::sort. + * + * @param a First source + * @param b Second source + * + * @return true if a is more important than b, else false + */ +bool InternalLightManager::compare_shadow_sources(const ShadowSource* a, const ShadowSource* b) const { + + // Make sure that sources which already have a region (but maybe outdated) + // come after sources which have no region at all. + if (a->has_region() != b->has_region()) { + return b->has_region(); + } + + // Compare sources based on their distance to the camera + PN_stdfloat dist_a = (_camera_pos - a->get_bounds().get_center()).length_squared(); + PN_stdfloat dist_b = (_camera_pos - a->get_bounds().get_center()).length_squared(); + + // XXX: Should also compare based on source size, so that huge sources recieve + // more updates + + return dist_b > dist_a; +} + +/** + * @brief Internal method to update all shadow sources + * @details This updates all shadow sources which are marked dirty. It will sort + * the list of all dirty shadow sources by their resolution, take the first + * n entries, and update them. The amount of sources processed depends on the + * max_updates of the ShadowManager. + */ +void InternalLightManager::update_shadow_sources() { + + // Find all dirty shadow sources and make a list of them + vector sources_to_update; + for (auto iter = _shadow_sources.begin(); iter != _shadow_sources.end(); ++iter) { + ShadowSource* source = *iter; + if (source) { + const BoundingSphere& bounds = source->get_bounds(); + + // Check if source is in range + PN_stdfloat distance_to_camera = (_camera_pos - bounds.get_center()).length() - bounds.get_radius(); + if (distance_to_camera < _shadow_update_distance) { + if (source->get_needs_update()) { + sources_to_update.push_back(source); + } + } else { + + // Free regions of sources which are out of the update radius, + // to make space for other regions + if (source->has_region()) { + _shadow_manager->get_atlas()->free_region(source->get_region()); + source->clear_region(); + } + } + } + + } + + // Sort the sources based on their importance, so that sources with a bigger + // priority come first. This helps to get a better packing on the shadow atlas. + // However, we also need to prioritize sources which have no current region, + // because no shadows are worse than outdated-shadows. + std::sort(sources_to_update.begin(), sources_to_update.end(), [this](const ShadowSource* a, const ShadowSource* b) { + return this->compare_shadow_sources(a, b); + }); + + // Get a handle to the atlas, will be frequently used + ShadowAtlas *atlas = _shadow_manager->get_atlas(); + + // Free the regions of all sources which will get updated. We have to take into + // account that only a limited amount of sources can get updated per frame. + size_t update_slots = min(sources_to_update.size(), + _shadow_manager->get_num_update_slots_left()); + for(size_t i = 0; i < update_slots; ++i) { + if (sources_to_update[i]->has_region()) { + atlas->free_region(sources_to_update[i]->get_region()); + } + } + + // Find an atlas spot for all regions which are supposed to get an update + for (size_t i = 0; i < update_slots; ++i) { + ShadowSource *source = sources_to_update[i]; + + if(!_shadow_manager->add_update(source)) { + // In case the ShadowManager lied about the number of updates left + lightmgr_cat.error() << "ShadowManager ensured update slot, but slot is taken!" << endl; + break; + } + + // We have an update slot, and are guaranteed to get updated as soon + // as possible, so we can start getting a new atlas position. + size_t region_size = atlas->get_required_tiles(source->get_resolution()); + LVecBase4i new_region = atlas->find_and_reserve_region(region_size, region_size); + LVecBase4 new_uv_region = atlas->region_to_uv(new_region); + source->set_region(new_region, new_uv_region); + + // Mark the source as updated + source->set_needs_update(false); + gpu_update_source(source); + } +} + +/** + * @brief Main update method + * @details This is the main update method of the InternalLightManager. It + * processes all lights and shadow sources, updates them, and notifies the + * GPU about it. This should be called on a per-frame basis. + * + * If the InternalLightManager was not initialized yet, an assertion is thrown. + */ +void InternalLightManager::update() { + nassertv(_shadow_manager != nullptr); // Not initialized yet! + nassertv(_cmd_list != nullptr); // Not initialized yet! + + update_lights(); + update_shadow_sources(); +} diff --git a/contrib/src/rplight/internalLightManager.h b/contrib/src/rplight/internalLightManager.h new file mode 100644 index 0000000000..572ce297a0 --- /dev/null +++ b/contrib/src/rplight/internalLightManager.h @@ -0,0 +1,102 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef INTERNALLIGHTMANAGER_H +#define INTERNALLIGHTMANAGER_H + +#include "referenceCount.h" +#include "rpLight.h" +#include "shadowSource.h" +#include "shadowAtlas.h" +#include "shadowManager.h" +#include "pointerSlotStorage.h" +#include "gpuCommandList.h" + +#define MAX_LIGHT_COUNT 65535 +#define MAX_SHADOW_SOURCES 2048 + +NotifyCategoryDecl(lightmgr, EXPORT_CLASS, EXPORT_TEMPL); + +/** + * @brief Internal class used for handling lights and shadows. + * @details This is the internal class used by the pipeline to handle all + * lights and shadows. It stores references to the lights, manages handling + * the light and shadow slots, and also communicates with the GPU with the + * GPUCommandQueue to store light and shadow source data. + */ +class InternalLightManager { +PUBLISHED: + InternalLightManager(); + + void add_light(PT(RPLight) light); + void remove_light(PT(RPLight) light); + + void update(); + inline void set_camera_pos(const LPoint3 &pos); + inline void set_shadow_update_distance(PN_stdfloat dist); + + inline int get_max_light_index() const; + MAKE_PROPERTY(max_light_index, get_max_light_index); + + inline size_t get_num_lights() const; + MAKE_PROPERTY(num_lights, get_num_lights); + + inline size_t get_num_shadow_sources() const; + MAKE_PROPERTY(num_shadow_sources, get_num_shadow_sources); + + inline void set_shadow_manager(ShadowManager* mgr); + inline ShadowManager* get_shadow_manager() const; + MAKE_PROPERTY(shadow_manager, get_shadow_manager, set_shadow_manager); + + inline void set_command_list(GPUCommandList *cmd_list); + +protected: + + void gpu_update_light(RPLight* light); + void gpu_update_source(ShadowSource* source); + void gpu_remove_light(RPLight* light); + void gpu_remove_consecutive_sources(ShadowSource *first_source, size_t num_sources); + + void setup_shadows(RPLight* light); + bool compare_shadow_sources(const ShadowSource* a, const ShadowSource* b) const; + + void update_lights(); + void update_shadow_sources(); + + GPUCommandList* _cmd_list; + ShadowManager* _shadow_manager; + + PointerSlotStorage _lights; + PointerSlotStorage _shadow_sources; + + LPoint3 _camera_pos; + float _shadow_update_distance; + +}; + +#include "internalLightManager.I" + +#endif // INTERNALLIGHTMANAGER_H diff --git a/contrib/src/rplight/p3rplight_composite1.cxx b/contrib/src/rplight/p3rplight_composite1.cxx new file mode 100644 index 0000000000..4bfca47bf7 --- /dev/null +++ b/contrib/src/rplight/p3rplight_composite1.cxx @@ -0,0 +1,13 @@ +#include "config_rplight.cxx" +#include "gpuCommand.cxx" +#include "gpuCommandList.cxx" +#include "iesDataset.cxx" +#include "internalLightManager.cxx" +#include "pssmCameraRig.cxx" +#include "rpLight.cxx" +#include "rpPointLight.cxx" +#include "rpSpotLight.cxx" +#include "shadowAtlas.cxx" +#include "shadowManager.cxx" +#include "shadowSource.cxx" +#include "tagStateManager.cxx" diff --git a/contrib/src/rplight/pointerSlotStorage.h b/contrib/src/rplight/pointerSlotStorage.h new file mode 100644 index 0000000000..11b4ace2bd --- /dev/null +++ b/contrib/src/rplight/pointerSlotStorage.h @@ -0,0 +1,228 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef POINTERSLOTSTORAGE_H +#define POINTERSLOTSTORAGE_H + + +#ifdef CPPPARSER + +// Dummy implementation for interrogate +template +class PointerSlotStorage {}; + +#else // CPPPARSER + + +#include "pandabase.h" +#include + +/** + * @brief Class to keep a list of pointers and nullpointers. + * @details This class stores a fixed size list of pointers, whereas pointers + * may be a nullptr as well. It provides functionality to find free slots, + * and also to find free consecutive slots, as well as taking care of reserving slots. + * + * @tparam T* Pointer-Type + * @tparam SIZE Size of the storage + */ +template +class PointerSlotStorage { +public: + /** + * @brief Constructs a new PointerSlotStorage + * @details This constructs a new PointerSlotStorage, with all slots + * initialized to a nullptr. + */ + PointerSlotStorage() { + _data.fill(nullptr); + _max_index = 0; + _num_entries = 0; + } + + /** + * @brief Returns the maximum index of the container + * @details This returns the greatest index of any element which is not zero. + * This can be useful for iterating the container, since all elements + * coming after the returned index are guaranteed to be a nullptr. + * + * If no elements are in this container, -1 is returned. + * @return Maximum index of the container + */ + int get_max_index() const { + return _max_index; + } + + /** + * @brief Returns the amount of elements of the container + * @details This returns the amount of elements in the container which are + * no nullptr. + * @return Amount of elements + */ + size_t get_num_entries() const { + return _num_entries; + } + + /** + * @brief Finds a free slot + * @details This finds the first slot which is a nullptr and returns it. + * This is most likely useful in combination with reserve_slot. + * + * When no slot found was found, slot will be undefined, and false will + * be returned. + * + * @param slot Output-Variable, slot will be stored there + * @return true if a slot was found, otherwise false + */ + bool find_slot(size_t &slot) const { + for (size_t i = 0; i < SIZE; ++i) { + if (_data[i] == nullptr) { + slot = i; + return true; + } + } + return false; + } + + /** + * @brief Finds free consecutive slots + * @details This behaves like find_slot, but it tries to find a slot + * after which free slots follow as well. + * + * When no slot found was found, slot will be undefined, and false will + * be returned. + * + * @param slot Output-Variable, index of the first slot of the consecutive + * slots will be stored there. + * @param num_consecutive Amount of consecutive slots to find, including the + * first slot. + * + * @return true if consecutive slots were found, otherwise false. + */ + bool find_consecutive_slots(size_t &slot, size_t num_consecutive) const { + nassertr(num_consecutive > 0, false); + + // Fall back to default search algorithm in case the parameters are equal + if (num_consecutive == 1) { + return find_slot(slot); + } + + // Try to find consecutive slots otherwise + for (size_t i = 0; i < SIZE; ++i) { + bool any_taken = false; + for (size_t k = 0; !any_taken && k < num_consecutive; ++k) { + any_taken = _data[i + k] != nullptr; + } + if (!any_taken) { + slot = i; + return true; + } + } + return false; + } + + /** + * @brief Frees an allocated slot + * @details This frees an allocated slot. If the slot was already freed + * before, this method throws an assertion. + * + * @param slot Slot to free + */ + void free_slot(size_t slot) { + nassertv(slot >= 0 && slot < SIZE); + nassertv(_data[slot] != nullptr); // Slot was already empty! + _data[slot] = nullptr; + _num_entries--; + + // Update maximum index + if (slot == _max_index) { + while (_max_index >= 0 && !_data[_max_index--]); + } + } + + /** + * @brief Frees consecutive allocated slots + * @details This behaves like PointerSlotStorage::free_slot, but deletes + * consecutive slots. + * + * @param slot Start of the consecutive slots to free + * @param num_consecutive Number of consecutive slots + */ + void free_consecutive_slots(size_t slot, size_t num_consecutive) { + for (size_t i = slot; i < slot + num_consecutive; ++i) { + free_slot(i); + } + } + + /** + * @brief Reserves a slot + * @details This reserves a slot by storing a pointer in it. If the slot + * was already taken, throws an assertion. + * If the ptr is a nullptr, also throws an assertion. + * If the slot was out of bounds, also throws an assertion. + * + * @param slot Slot to reserve + * @param ptr Pointer to store + */ + void reserve_slot(size_t slot, T ptr) { + nassertv(slot >= 0 && slot < SIZE); + nassertv(_data[slot] == nullptr); // Slot already taken! + nassertv(ptr != nullptr); // nullptr passed as argument! + _max_index = max(_max_index, (int)slot); + _data[slot] = ptr; + _num_entries++; + } + + typedef array InternalContainer; + + /** + * @brief Returns an iterator to the begin of the container + * @details This returns an iterator to the beginning of the container + * @return Begin-Iterator + */ + typename InternalContainer::iterator begin() { + return _data.begin(); + } + + /** + * @brief Returns an iterator to the end of the container + * @details This returns an iterator to the end of the iterator. This only + * iterates to PointerSlotStorage::get_max_index() + * @return [description] + */ + typename InternalContainer::iterator end() { + return _data.begin() + _max_index + 1; + } + +private: + int _max_index; + size_t _num_entries; + InternalContainer _data; +}; + +#endif // CPPPARSER + +#endif // POINTERSLOTSTORAGE_H diff --git a/contrib/src/rplight/pssmCameraRig.I b/contrib/src/rplight/pssmCameraRig.I new file mode 100644 index 0000000000..cd87718262 --- /dev/null +++ b/contrib/src/rplight/pssmCameraRig.I @@ -0,0 +1,243 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +/** + * @brief Sets the maximum pssm distance. + * @details This sets the maximum distance in world space until which shadows + * are rendered. After this distance, no shadows will be rendered. + * + * If the distance is below zero, an assertion is triggered. + * + * @param distance Maximum distance in world space + */ +inline void PSSMCameraRig::set_pssm_distance(float distance) { + nassertv(distance > 0.0 && distance < 100000.0); + _pssm_distance = distance; +} + +/** + * @brief Sets the suns distance + * @details This sets the distance the cameras will have from the cameras frustum. + * This prevents far objects from having no shadows, which can occur when these + * objects are between the cameras frustum and the sun, but not inside of the + * cameras frustum. Setting the sun distance high enough will move the cameras + * away from the camera frustum, being able to cover those distant objects too. + * + * If the sun distance is set too high, artifacts will occur due to the reduced + * range of depth. If a value below zero is passed, an assertion will get + * triggered. + * + * @param distance The sun distance + */ +inline void PSSMCameraRig::set_sun_distance(float distance) { + nassertv(distance > 0.0 && distance < 100000.0); + _sun_distance = distance; +} + +/** + * @brief Sets the logarithmic factor + * @details This sets the logarithmic factor, which is the core of the algorithm. + * PSSM splits the camera frustum based on a linear and a logarithmic factor. + * While a linear factor provides a good distribution, it often is not applicable + * for wider distances. A logarithmic distribution provides a better distribution + * at distance, but suffers from splitting in the near areas. + * + * The logarithmic factor mixes the logarithmic and linear split distribution, + * to get the best of both. A greater factor will make the distribution more + * logarithmic, while a smaller factor will make it more linear. + * + * If the factor is below zero, an ssertion is triggered. + * + * @param factor The logarithmic factor + */ +inline void PSSMCameraRig::set_logarithmic_factor(float factor) { + nassertv(factor > 0.0); + _logarithmic_factor = factor; +} + +/** + * @brief Sets whether to use a fixed film size + * @details This controls if a fixed film size should be used. This will cause + * the camera rig to cache the current film size, and only change it in case + * it gets too small. This provides less flickering when moving, because the + * film size will stay roughly constant. However, to prevent the cached film + * size getting too big, one should call PSSMCameraRig::reset_film_size + * once in a while, otherwise there might be a lot of wasted space. + * + * @param flag Whether to use a fixed film size + */ +inline void PSSMCameraRig::set_use_fixed_film_size(bool flag) { + _use_fixed_film_size = flag; +} + +/** + * @brief Sets the resolution of each split + * @details This sets the resolution of each split. Currently it is equal for + * each split. This is required when using PSSMCameraRig::set_use_stable_csm, + * to compute how bix a texel is. + * + * It has to match the y-resolution of the pssm shadow map. If an invalid + * resolution is triggered, an assertion is thrown. + * + * @param resolution The resolution of each split. + */ +inline void PSSMCameraRig::set_resolution(size_t resolution) { + nassertv(resolution >= 0 && resolution < 65535); + _resolution = resolution; +} + +/** + * @brief Sets whether to use stable CSM snapping. + * @details This option controls if stable CSM snapping should be used. When the + * option is enabled, all splits will snap to their texels, so that when moving, + * no flickering will occur. However, this only works when the splits do not + * change their film size, rotation and angle. + * + * @param flag Whether to use stable CSM snapping + */ +inline void PSSMCameraRig::set_use_stable_csm(bool flag) { + _use_stable_csm = flag; +} + +/** + * @brief Sets the border bias for each split + * @details This sets the border bias for every split. This increases each + * splits frustum by multiplying it by (1 + bias), and helps reducing artifacts + * at the borders of the splits. Artifacts can occur when the bias is too low, + * because then the filtering will go over the bounds of the split, producing + * invalid results. + * + * If the bias is below zero, an assertion is thrown. + * + * @param bias Border bias + */ +inline void PSSMCameraRig::set_border_bias(float bias) { + nassertv(bias >= 0.0); + _border_bias = bias; +} + +/** + * @brief Resets the film size cache + * @details In case PSSMCameraRig::set_use_fixed_film_size is used, this resets + * the film size cache. This might lead to a small "jump" in the shadows, + * because the film size changes, however it leads to a better shadow distribution. + * + * This is the case because when using a fixed film size, the cache will get + * bigger and bigger, whenever the camera moves to a grazing angle. However, + * when moving back to a normal angle, the film size cache still stores this + * big angle, and thus the splits will have a much bigger film size than actualy + * required. To prevent this, call this method once in a while, so an optimal + * distribution is ensured. + */ +inline void PSSMCameraRig::reset_film_size_cache() { + for (size_t i = 0; i < _max_film_sizes.size(); ++i) { + _max_film_sizes[i].fill(0); + } +} + +/** + * @brief Returns the n-th camera + * @details This returns the n-th camera of the camera rig, which can be used + * for various stuff like showing its frustum, passing it as a shader input, + * and so on. + * + * The first camera is the camera which is the camera of the first split, + * which is the split closest to the camera. All cameras follow in descending + * order until to the last camera, which is the split furthest away from the + * camera. + * + * If an invalid index is passed, an assertion is thrown. + * + * @param index Index of the camera. + * @return [description] + */ +inline NodePath PSSMCameraRig::get_camera(size_t index) { + nassertr(index >= 0 && index < _cam_nodes.size(), NodePath()); + return _cam_nodes[index]; +} + +/** + * @brief Internal method to compute the distance of a split + * @details This is the internal method to perform the weighting of the + * logarithmic and linear distribution. It computes the distance to the + * camera from which a given split starts, by weighting the logarithmic and + * linear factor. + * + * The return value is a value ranging from 0 .. 1. To get the distance in + * world space, the value has to get multiplied with the maximum shadow distance. + * + * @param split_index The index of the split + * @return Distance of the split, ranging from 0 .. 1 + */ +inline float PSSMCameraRig::get_split_start(size_t split_index) { + float x = (float)split_index / (float)_cam_nodes.size(); + return (exp(_logarithmic_factor*x)-1) / (exp(_logarithmic_factor)-1); +} + +/** + * @brief Internal method for interpolating a point along the camera frustum + * @details This method takes a given distance in the 0 .. 1 range, whereas + * 0 denotes the camera near plane, and 1 denotes the camera far plane, + * and lineary interpolates between them. + * + * @param origin Edge of the frustum + * @param depth Distance in the 0 .. 1 range + * + * @return interpolated point in world space + */ +inline LPoint3 PSSMCameraRig::get_interpolated_point(CoordinateOrigin origin, float depth) { + nassertr(depth >= 0.0 && depth <= 1.0, LPoint3()); + return _curr_near_points[origin] * (1.0 - depth) + _curr_far_points[origin] * depth; +} + +/** + * @brief Returns a handle to the MVP array + * @details This returns a handle to the array of view-projection matrices + * of the different splits. This can be used for computing shadows. The array + * is a PTALMatrix4 and thus can be directly bound to a shader. + * + * @return view-projection matrix array + */ +inline const PTA_LMatrix4 &PSSMCameraRig::get_mvp_array() { + return _camera_mvps; +} + +/** + * @brief Returns a handle to the near and far planes array + * @details This returns a handle to the near and far plane array. Each split + * has an entry in the array, whereas the x component of the vecto denotes the + * near plane, and the y component denotes the far plane of the split. + * + * This is required because the near and far planes of the splits change + * constantly. To access them in a shader, the shader needs access to the + * array. + * + * @return Array of near and far planes + */ +inline const PTA_LVecBase2 &PSSMCameraRig::get_nearfar_array() { + return _camera_nearfar; +} diff --git a/contrib/src/rplight/pssmCameraRig.cxx b/contrib/src/rplight/pssmCameraRig.cxx new file mode 100644 index 0000000000..98da3840f3 --- /dev/null +++ b/contrib/src/rplight/pssmCameraRig.cxx @@ -0,0 +1,396 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "pssmCameraRig.h" + +#define _USE_MATH_DEFINES +#include +#include "orthographicLens.h" + + +PStatCollector PSSMCameraRig::_update_collector("App:Show code:RP_PSSM_update"); + +/** + * @brief Constructs a new PSSM camera rig + * @details This constructs a new camera rig, with a given amount of splits. + * The splits can not be changed later on. Splits are also called Cascades. + * + * An assertion will be triggered if the splits are below zero. + * + * @param num_splits Amount of PSSM splits + */ +PSSMCameraRig::PSSMCameraRig(size_t num_splits) { + nassertv(num_splits > 0); + _num_splits = num_splits; + _pssm_distance = 100.0; + _sun_distance = 500.0; + _use_fixed_film_size = false; + _use_stable_csm = true; + _logarithmic_factor = 1.0; + _resolution = 512; + _border_bias = 0.1; + _camera_mvps = PTA_LMatrix4::empty_array(num_splits); + _camera_nearfar = PTA_LVecBase2::empty_array(num_splits); + init_cam_nodes(); +} + +/** + * @brief Destructs the camera rig + * @details This destructs the camera rig, cleaning up all used resources. + */ +PSSMCameraRig::~PSSMCameraRig() { + // TODO: Detach all cameras and call remove_node. Most likely this is not + // an issue tho, because the camera rig will never get destructed. +} + +/** + * @brief Internal method to init the cameras + * @details This method constructs all cameras and their required lens nodes + * for all splits. It also resets the film size array. + */ +void PSSMCameraRig::init_cam_nodes() { + _cam_nodes.reserve(_num_splits); + _max_film_sizes.resize(_num_splits); + _cameras.resize(_num_splits); + for (size_t i = 0; i < _num_splits; ++i) + { + // Construct a new lens + Lens *lens = new OrthographicLens(); + lens->set_film_size(1, 1); + lens->set_near_far(1, 1000); + + // Construct a new camera + _cameras[i] = new Camera("pssm-cam-" + to_string(((long long)i)), lens); + _cam_nodes.push_back(NodePath(_cameras[i])); + _max_film_sizes[i].fill(0); + } +} + +/** + * @brief Reparents the camera rig + * @details This reparents all cameras to the given parent. Usually the parent + * will be ShowBase.render. The parent should be the same node where the + * main camera is located in, too. + * + * If an empty parrent is passed, an assertion will get triggered. + * + * @param parent Parent node path + */ +void PSSMCameraRig::reparent_to(NodePath parent) { + nassertv(!parent.is_empty()); + for (size_t i = 0; i < _num_splits; ++i) { + _cam_nodes[i].reparent_to(parent); + } + _parent = parent; +} + +/** + * @brief Internal method to compute the view-projection matrix of a camera + * @details This returns the view-projection matrix of the given split. No bounds + * checking is done. If an invalid index is passed, undefined behaviour occurs. + * + * @param split_index Index of the split + * @return view-projection matrix of the split + */ +LMatrix4 PSSMCameraRig::compute_mvp(size_t split_index) { + LMatrix4 transform = _parent.get_transform(_cam_nodes[split_index])->get_mat(); + return transform * _cameras[split_index]->get_lens()->get_projection_mat(); +} + +/** + * @brief Internal method used for stable CSM + * @details This method is used when stable CSM is enabled. It ensures that each + * source only moves in texel-steps, thus preventing flickering. This works by + * projecting the point (0, 0, 0) to NDC space, making sure that it gets projected + * to a texel center, and then projecting that texel back. + * + * This only works if the camera does not rotate, change its film size, or change + * its angle. + * + * @param mat view-projection matrix of the camera + * @param resolution resolution of the split + * + * @return Offset to add to the camera position to achieve stable snapping + */ +LVecBase3 PSSMCameraRig::get_snap_offset(const LMatrix4& mat, size_t resolution) { + // Transform origin to camera space + LPoint4 base_point = mat.get_row(3) * 0.5 + 0.5; + + // Compute the snap offset + float texel_size = 1.0 / (float)(resolution); + float offset_x = fmod(base_point.get_x(), texel_size); + float offset_y = fmod(base_point.get_y(), texel_size); + + // Reproject the offset back, for that we need the inverse MVP + LMatrix4 inv_mat(mat); + inv_mat.invert_in_place(); + LVecBase3 new_base_point = inv_mat.xform_point(LVecBase3( + (base_point.get_x() - offset_x) * 2.0 - 1.0, + (base_point.get_y() - offset_y) * 2.0 - 1.0, + base_point.get_z() * 2.0 - 1.0 + )); + return -new_base_point; +} + +/** + * @brief Computes the average of a list of points + * @details This computes the average over a given set of points in 3D space. + * It returns the average of those points, namely sum_of_points / num_points. + * + * It is designed to work with a frustum, which is why it takes two arrays + * with a dimension of 4. Usually the first array are the camera near points, + * and the second array are the camera far points. + * + * @param starts First array of points + * @param ends Second array of points + * @return Average of points + */ +LPoint3 get_average_of_points(LVecBase3 const (&starts)[4], LVecBase3 const (&ends)[4]) { + LPoint3 mid_point(0, 0, 0); + for (size_t k = 0; k < 4; ++k) { + mid_point += starts[k]; + mid_point += ends[k]; + } + return mid_point / 8.0; +} + +/** + * @brief Finds the minimum and maximum extends of the given projection + * @details This projects each point of the given array of points using the + * cameras view-projection matrix, and computes the minimum and maximum + * of the projected points. + * + * @param min_extent Will store the minimum extent of the projected points in NDC space + * @param max_extent Will store the maximum extent of the projected points in NDC space + * @param transform The transformation matrix of the camera + * @param proj_points The array of points to project + * @param cam The camera to be used to project the points + */ +void find_min_max_extents(LVecBase3 &min_extent, LVecBase3 &max_extent, const LMatrix4 &transform, LVecBase3 const (&proj_points)[8], Camera *cam) { + + min_extent.fill(1e10); + max_extent.fill(-1e10); + LPoint2 screen_points[8]; + + // Now project all points to the screen space of the current camera and also + // find the minimum and maximum extents + for (size_t k = 0; k < 8; ++k) { + LVecBase4 point(proj_points[k], 1); + LPoint4 proj_point = transform.xform(point); + LPoint3 proj_point_3d(proj_point.get_x(), proj_point.get_y(), proj_point.get_z()); + cam->get_lens()->project(proj_point_3d, screen_points[k]); + + // Find min / max extents + if (screen_points[k].get_x() > max_extent.get_x()) max_extent.set_x(screen_points[k].get_x()); + if (screen_points[k].get_y() > max_extent.get_y()) max_extent.set_y(screen_points[k].get_y()); + + if (screen_points[k].get_x() < min_extent.get_x()) min_extent.set_x(screen_points[k].get_x()); + if (screen_points[k].get_y() < min_extent.get_y()) min_extent.set_y(screen_points[k].get_y()); + + // Find min / max projected depth to adjust far plane + if (proj_point.get_y() > max_extent.get_z()) max_extent.set_z(proj_point.get_y()); + if (proj_point.get_y() < min_extent.get_z()) min_extent.set_z(proj_point.get_y()); + } +} + +/** + * @brief Computes a film size from a given minimum and maximum extend + * @details This takes a minimum and maximum extent in NDC space and computes + * the film size and film offset needed to cover that extent. + * + * @param film_size Output film size, can be used for Lens::set_film_size + * @param film_offset Output film offset, can be used for Lens::set_film_offset + * @param min_extent Minimum extent + * @param max_extent Maximum extent + */ +inline void get_film_properties(LVecBase2 &film_size, LVecBase2 &film_offset, const LVecBase3 &min_extent, const LVecBase3 &max_extent) { + float x_center = (min_extent.get_x() + max_extent.get_x()) * 0.5; + float y_center = (min_extent.get_y() + max_extent.get_y()) * 0.5; + float x_size = max_extent.get_x() - x_center; + float y_size = max_extent.get_y() - y_center; + film_size.set(x_size, y_size); + film_offset.set(x_center * 0.5, y_center * 0.5); +} + +/** + * @brief Merges two arrays + * @details This takes two arrays which each 4 members and produces an array + * with both arrays contained. + * + * @param dest Destination array + * @param array1 First array + * @param array2 Second array + */ +inline void merge_points_interleaved(LVecBase3 (&dest)[8], LVecBase3 const (&array1)[4], LVecBase3 const (&array2)[4]) { + for (size_t k = 0; k < 4; ++k) { + dest[k] = array1[k]; + dest[k+4] = array2[k]; + } +} + + +/** + * @brief Internal method to compute the splits + * @details This is the internal update method to update the PSSM splits. + * It distributes the camera splits over the frustum, and updates the + * MVP array aswell as the nearfar array. + * + * @param transform Main camera transform + * @param max_distance Maximum pssm distance, relative to the camera far plane + * @param light_vector Sun-Vector + */ +void PSSMCameraRig::compute_pssm_splits(const LMatrix4& transform, float max_distance, const LVecBase3& light_vector) { + nassertv(!_parent.is_empty()); + + // PSSM Distance should never be smaller than camera far plane. + nassertv(max_distance <= 1.0); + + float filmsize_bias = 1.0 + _border_bias; + + // Compute the positions of all cameras + for (size_t i = 0; i < _cam_nodes.size(); ++i) { + float split_start = get_split_start(i) * max_distance; + float split_end = get_split_start(i + 1) * max_distance; + + LVecBase3 start_points[4]; + LVecBase3 end_points[4]; + LVecBase3 proj_points[8]; + + // Get split bounding box, and collect all points which define the frustum + for (size_t k = 0; k < 4; ++k) { + start_points[k] = get_interpolated_point((CoordinateOrigin)k, split_start); + end_points[k] = get_interpolated_point((CoordinateOrigin)k, split_end); + proj_points[k] = start_points[k]; + proj_points[k + 4] = end_points[k]; + } + + // Compute approximate split mid point + LPoint3 split_mid = get_average_of_points(start_points, end_points); + LPoint3 cam_start = split_mid + light_vector * _sun_distance; + + // Reset the film size, offset and far-plane + Camera* cam = DCAST(Camera, _cam_nodes[i].node()); + cam->get_lens()->set_film_size(1, 1); + cam->get_lens()->set_film_offset(0, 0); + cam->get_lens()->set_near_far(1, 100); + + // Find a good initial position + _cam_nodes[i].set_pos(cam_start); + _cam_nodes[i].look_at(split_mid); + + LVecBase3 best_min_extent, best_max_extent; + + // Find minimum and maximum extents of the points + LMatrix4 merged_transform = _parent.get_transform(_cam_nodes[i])->get_mat(); + find_min_max_extents(best_min_extent, best_max_extent, merged_transform, proj_points, cam); + + // Find the film size to cover all points + LVecBase2 film_size, film_offset; + get_film_properties(film_size, film_offset, best_min_extent, best_max_extent); + + if (_use_fixed_film_size) { + // In case we use a fixed film size, store the maximum film size, and + // only change the film size if a new maximum is there + if (_max_film_sizes[i].get_x() < film_size.get_x()) _max_film_sizes[i].set_x(film_size.get_x()); + if (_max_film_sizes[i].get_y() < film_size.get_y()) _max_film_sizes[i].set_y(film_size.get_y()); + + cam->get_lens()->set_film_size(_max_film_sizes[i] * filmsize_bias); + } else { + // If we don't use a fixed film size, we can just set the film size + // on the lens. + cam->get_lens()->set_film_size(film_size * filmsize_bias); + } + + // Compute new film offset + cam->get_lens()->set_film_offset(film_offset); + cam->get_lens()->set_near_far(10, best_max_extent.get_z()); + _camera_nearfar[i] = LVecBase2(10, best_max_extent.get_z()); + + // Compute the camera MVP + LMatrix4 mvp = compute_mvp(i); + + // Stable CSM Snapping + if (_use_stable_csm) { + LPoint3 snap_offset = get_snap_offset(mvp, _resolution); + _cam_nodes[i].set_pos(_cam_nodes[i].get_pos() + snap_offset); + + // Compute the new mvp, since we changed the snap offset + mvp = compute_mvp(i); + } + + _camera_mvps.set_element(i, mvp); + } +} + + +/** + * @brief Updates the PSSM camera rig + * @details This updates the rig with an updated camera position, and a given + * light vector. This should be called on a per-frame basis. It will reposition + * all camera sources to fit the frustum based on the pssm distribution. + * + * The light vector should be the vector from the light source, not the + * vector to the light source. + * + * @param cam_node Target camera node + * @param light_vector The vector from the light to any point + */ +void PSSMCameraRig::update(NodePath cam_node, const LVecBase3 &light_vector) { + nassertv(!cam_node.is_empty()); + _update_collector.start(); + + // Get camera node transform + LMatrix4 transform = cam_node.get_transform()->get_mat(); + + // Get Camera and Lens pointers + Camera* cam = DCAST(Camera, cam_node.get_child(0).node()); + nassertv(cam != nullptr); + Lens* lens = cam->get_lens(); + + // Extract near and far points: + lens->extrude(LPoint2(-1, 1), _curr_near_points[UpperLeft], _curr_far_points[UpperLeft]); + lens->extrude(LPoint2(1, 1), _curr_near_points[UpperRight], _curr_far_points[UpperRight]); + lens->extrude(LPoint2(-1, -1), _curr_near_points[LowerLeft], _curr_far_points[LowerLeft]); + lens->extrude(LPoint2(1, -1), _curr_near_points[LowerRight], _curr_far_points[LowerRight]); + + // Construct MVP to project points to world space + LMatrix4 mvp = transform * lens->get_view_mat(); + + // Project all points to world space + for (size_t i = 0; i < 4; ++i) { + LPoint4 ws_near = mvp.xform(_curr_near_points[i]); + LPoint4 ws_far = mvp.xform(_curr_far_points[i]); + _curr_near_points[i].set(ws_near.get_x(), ws_near.get_y(), ws_near.get_z()); + _curr_far_points[i].set(ws_far.get_x(), ws_far.get_y(), ws_far.get_z()); + } + + // Do the actual PSSM + compute_pssm_splits( transform, _pssm_distance / lens->get_far(), light_vector ); + + _update_collector.stop(); +} + diff --git a/contrib/src/rplight/pssmCameraRig.h b/contrib/src/rplight/pssmCameraRig.h new file mode 100644 index 0000000000..ce0567bbbf --- /dev/null +++ b/contrib/src/rplight/pssmCameraRig.h @@ -0,0 +1,125 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef PSSMCAMERARIG_H +#define PSSMCAMERARIG_H + +#include "pandabase.h" +#include "luse.h" +#include "camera.h" +#include "nodePath.h" +#include "pStatCollector.h" + +#include + +/** + * @brief Main class used for handling PSSM + * @details This is the main class for supporting PSSM, it is used by the PSSM + * plugin to compute the position of the splits. + * + * It supports handling a varying amount of cameras, and fitting those cameras + * into the main camera frustum, to render distant shadows. It also supports + * various optimizations for fitting the frustum, e.g. rotating the sources + * to get a better coverage. + * + * It also provides methods to get arrays of data about the used cameras + * view-projection matrices and their near and far plane, which is required for + * processing the data in the shadow sampling shader. + * + * In this class, there is often referred to "Splits" or also called "Cascades". + * These denote the different cameras which are used to split the frustum, + * and are a common term related to the PSSM algorithm. + * + * To understand the functionality of this class, a detailed knowledge of the + * PSSM algorithm is helpful. + */ +class PSSMCameraRig { +PUBLISHED: + PSSMCameraRig(size_t num_splits); + ~PSSMCameraRig(); + + inline void set_pssm_distance(float distance); + inline void set_sun_distance(float distance); + inline void set_use_fixed_film_size(bool flag); + inline void set_resolution(size_t resolution); + inline void set_use_stable_csm(bool flag); + inline void set_logarithmic_factor(float factor); + inline void set_border_bias(float bias); + + void update(NodePath cam_node, const LVecBase3 &light_vector); + inline void reset_film_size_cache(); + + inline NodePath get_camera(size_t index); + + void reparent_to(NodePath parent); + inline const PTA_LMatrix4 &get_mvp_array(); + inline const PTA_LVecBase2 &get_nearfar_array(); + +public: + // Used to access the near and far points in the array + enum CoordinateOrigin { + UpperLeft = 0, + UpperRight, + LowerLeft, + LowerRight + }; + +protected: + void init_cam_nodes(); + void compute_pssm_splits(const LMatrix4& transform, float max_distance, + const LVecBase3 &light_vector); + inline float get_split_start(size_t split_index); + LMatrix4 compute_mvp(size_t cam_index); + inline LPoint3 get_interpolated_point(CoordinateOrigin origin, float depth); + LVecBase3 get_snap_offset(const LMatrix4& mat, size_t resolution); + + vector _cam_nodes; + vector _cameras; + vector _max_film_sizes; + + // Current near and far points + // Order: UL, UR, LL, LR (See CoordinateOrigin) + LPoint3 _curr_near_points[4]; + LPoint3 _curr_far_points[4]; + float _pssm_distance; + float _sun_distance; + float _logarithmic_factor; + float _border_bias; + bool _use_fixed_film_size; + bool _use_stable_csm; + size_t _resolution; + size_t _num_splits; + NodePath _parent; + + PTA_LMatrix4 _camera_mvps; + PTA_LVecBase2 _camera_nearfar; + + static PStatCollector _update_collector; +}; + +#include "pssmCameraRig.I" + +#endif // PSSMCAMERARIG_H diff --git a/contrib/src/rplight/rpLight.I b/contrib/src/rplight/rpLight.I new file mode 100644 index 0000000000..9b21906310 --- /dev/null +++ b/contrib/src/rplight/rpLight.I @@ -0,0 +1,406 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +/** + * @brief Returns the amount of shadow sources + * @details This returns the amount of shadow sources attached to this light. + * In case the light has no shadows enabled, or the light was not attached + * yet, this returns 0. + * + * @return Amount of shadow sources + */ +inline int RPLight::get_num_shadow_sources() const { + return _shadow_sources.size(); +} + +/** + * @brief Returns the n-th shadow source + * @details This returns the n-th attached shadow source. This ranges from + * 0 .. RPLight::get_num_shadow_sources(). If an invalid index is passed, + * an assertion is thrown. + * + * @param index Index of the source + * @return Handle to the shadow source + */ +inline ShadowSource* RPLight::get_shadow_source(size_t index) const { + nassertr(index < _shadow_sources.size(), nullptr); // Invalid shadow source index + return _shadow_sources[index]; +} + +/** + * @brief Clears all shadow source + * @details This removes and destructs all shadow sources attached to this light. + * This usually gets called when the light gets detached or destructed. + * All shadows sources are freed, and then removed from the shadow source list. + */ +inline void RPLight::clear_shadow_sources() { + for (size_t i = 0; i < _shadow_sources.size(); ++i) { + delete _shadow_sources[i]; + } + _shadow_sources.clear(); +} + +/** + * @brief Sets whether the light needs an update + * @details This controls whether the light needs to get an update. This is the + * case when a property of the light changed, e.g. position or color. It does + * not affect the shadows (For that use RPLight::invalidate_shadows()). + * When this flag is set to true, the light will get resubmitted to the GPU + * in the next update cycle. + * + * You should usually never set the flag to false manually. The + * InternalLightManager will do this when the data got sucessfully updated. + * + * @param flag Update-Flag + */ +inline void RPLight::set_needs_update(bool flag) { + _needs_update = flag; +} + +/** + * @brief Returns whether the light needs an update + * @details This returns whether the light needs an update. This might be the + * case when a property of the light was changed, e.g. position or color. + * It does not affect the shadows, you have to query the update flag of each + * individual source for that. + * The return value is the value previously set with RPLight::set_needs_update. + * + * @return Update-flag + */ +inline bool RPLight::get_needs_update() const { + return _needs_update; +} + +/** + * @brief Returns whether the light has a slot + * @details This returns wheter the light currently is attached, and thus has + * a slot in the InternalLightManagers light list. When the light is attached, + * this returns true, otherwise it will return false. + * + * @return true if the light has a slot, false otherwise + */ +inline bool RPLight::has_slot() const { + return _slot >= 0; +} + +/** + * @brief Returns the slot of the light + * @details This returns the slot of the light. This is the space on the GPU + * where the light is stored. If the light is not attached yet, this will + * return -1, otherwise the index of the light. + * + * @return Light-Slot + */ +inline int RPLight::get_slot() const { + return _slot; +} + +/** + * @brief Removes the light slot + * @details This is an internal method to remove the slot of the light. It gets + * called by the InternalLightManager when a light gets detached. It internally + * sets the slot to -1 to indicate the light is no longer attached. + */ +inline void RPLight::remove_slot() { + _slot = -1; +} + +/** + * @brief Assigns a slot to the light + * @details This assigns a slot to the light, marking it as attached. The slot + * relates to the index in the GPU's storage of lights. This is an internal + * method called by the InternalLightManager when the light got attached. + * + * @param slot Slot of the light + */ +inline void RPLight::assign_slot(int slot) { + _slot = slot; +} + +/** + * @brief Invalidates the shadows + * @details This invalidates all shadows of the light, causing them to get + * regenerated. This might be the case when the lights position or similar + * changed. This will cause all shadow sources to be updated, emitting a + * shadow update. Be careful when calling this method if you don't want all + * sources to get updated. If you only have to invalidate a single shadow source, + * use get_shadow_source(n)->set_needs_update(true). + */ +inline void RPLight::invalidate_shadows() { + for (size_t i = 0; i < _shadow_sources.size(); ++i) { + _shadow_sources[i]->set_needs_update(true); + } +} + +/** + * @brief Sets the position of the light + * @details This sets the position of the light in world space. It will cause + * the light to get invalidated, and resubmitted to the GPU. + * + * @param pos Position in world space + */ +inline void RPLight::set_pos(const LVecBase3 &pos) { + set_pos(pos.get_x(), pos.get_y(), pos.get_z()); +} + +/** + * @brief Sets the position of the light + * @details @copydetails RPLight::set_pos(const LVecBase3 &pos) + * + * @param x X-component of the position + * @param y Y-component of the position + * @param z Z-component of the position + */ +inline void RPLight::set_pos(float x, float y, float z) { + _position.set(x, y, z); + set_needs_update(true); + invalidate_shadows(); +} + +/** + * @brief Returns the position of the light + * @details This returns the position of the light previously set with + * RPLight::set_pos(). The returned position is in world space. + * @return Light-position + */ +inline const LVecBase3& RPLight::get_pos() const { + return _position; +} + +/** + * @brief Sets the lights color + * @details This sets the lights color. The color should not include the brightness + * of the light, you should control that with the energy. The color specifies + * the lights "tint" and will get multiplied with its specular and diffuse + * contribution. + * + * The color will be normalized by dividing by the colors luminance. Setting + * higher values than 1.0 will have no effect. + * + * @param color Light color + */ +inline void RPLight::set_color(const LVecBase3 &color) { + _color = color; + _color /= 0.2126 * color.get_x() + 0.7152 * color.get_y() + 0.0722 * color.get_z(); + set_needs_update(true); +} + +/** + * @brief Sets the lights color + * @details @copydetails RPLight::set_color(const LVecBase3 &color) + * + * @param r Red-component of the color + * @param g Green-component of the color + * @param b Blue-component of the color + */ +inline void RPLight::set_color(float r, float g, float b) { + set_color(LVecBase3(r, g, b)); +} + +/** + * @brief Returns the lights color + * @details This returns the light color, previously set with RPLight::set_color. + * This does not include the energy of the light. It might differ from what + * was set with set_color, because the color is normalized by dividing it + * by its luminance. + * @return Light-color + */ +inline const LVecBase3& RPLight::get_color() const { + return _color; +} + +/** + * @brief Sets the energy of the light + * @details This sets the energy of the light, which can be seen as the brightness + * of the light. It will get multiplied with the normalized color. + * + * @param energy energy of the light + */ +inline void RPLight::set_energy(float energy) { + _energy = energy; + set_needs_update(true); +} + +/** + * @brief Returns the energy of the light + * @details This returns the energy of the light, previously set with + * RPLight::set_energy. + * + * @return energy of the light + */ +inline float RPLight::get_energy() const { + return _energy; +} + +/** + * @brief Returns the type of the light + * @details This returns the internal type of the light, which was specified + * in the lights constructor. This can be used to distinguish between light + * types. + * @return Type of the light + */ +inline RPLight::LightType RPLight::get_light_type() const { + return _light_type; +} + +/** + * @brief Controls whether the light casts shadows + * @details This sets whether the light casts shadows. You can not change this + * while the light is attached. When flag is set to true, the light will be + * setup to cast shadows, spawning shadow sources based on the lights type. + * If the flag is set to false, the light will be inddicated to cast no shadows. + * + * @param flag Whether the light casts shadows + */ +inline void RPLight::set_casts_shadows(bool flag) { + if (has_slot()) { + cerr << "Light is already attached, can not call set_casts_shadows!" << endl; + return; + } + _casts_shadows = flag; +} + +/** + * @brief Returns whether the light casts shadows + * @details This returns whether the light casts shadows, the returned value + * is the one previously set with RPLight::set_casts_shadows. + * + * @return true if the light casts shadows, false otherwise + */ +inline bool RPLight::get_casts_shadows() const { + return _casts_shadows; +} + +/** + * @brief Sets the lights shadow map resolution + * @details This sets the lights shadow map resolution. This has no effect + * when the light is not told to cast shadows (Use RPLight::set_casts_shadows). + * + * When calling this on a light with multiple shadow sources (e.g. PointLight), + * this controls the resolution of each source. If the light has 6 shadow sources, + * and you use a resolution of 512x512, the lights shadow map will occur a + * space of 6 * 512x512 maps in the shadow atlas. + * + * @param resolution Resolution of the shadow map in pixels + */ +inline void RPLight::set_shadow_map_resolution(size_t resolution) { + nassertv(resolution >= 32 && resolution <= 16384); + _source_resolution = resolution; + invalidate_shadows(); +} + +/** + * @brief Returns the shadow map resolution + * @details This returns the shadow map resolution of each source of the light. + * If the light is not setup to cast shadows, this value is meaningless. + * The returned value is the one previously set with RPLight::set_shadow_map_resolution. + * + * @return Shadow map resolution in pixels + */ +inline size_t RPLight::get_shadow_map_resolution() const { + return _source_resolution; +} + +/** + * @brief Sets the ies profile + * @details This sets the ies profile of the light. The parameter should be a + * handle previously returned by RenderPipeline.load_ies_profile. Using a + * value of -1 indicates no ies profile. + * + * Notice that for ies profiles which cover a whole range, you should use + * PointLights, whereas for ies profiles which only cover the lower hemisphere + * you should use SpotLights for the best performance. + * + * @param profile IES Profile handle + */ +inline void RPLight::set_ies_profile(int profile) { + _ies_profile = profile; + set_needs_update(true); +} + +/** + * @brief Returns the lights ies profile + * @details This returns the ies profile of a light, previously set with + * RPLight::set_ies_profile. In case no ies profile was set, returns -1. + * + * @return IES Profile handle + */ +inline int RPLight::get_ies_profile() const { + return _ies_profile; +} + +/** + * @brief Returns whether the light has an ies profile assigned + * @details This returns whether the light has an ies profile assigned, + * previously done with RPLight::set_ies_profile. + * + * @return true if the light has an ies profile assigned, false otherwise + */ +inline bool RPLight::has_ies_profile() const { + return _ies_profile >= 0; +} + +/** + * @brief Clears the ies profile + * @details This clears the ies profile of the light, telling it to no longer + * use an ies profile, and instead use the default attenuation. + */ +inline void RPLight::clear_ies_profile() { + set_ies_profile(-1); +} + +/** + * @brief Sets the near plane of the light + * @details This sets the near plane of all shadow sources of the light. It has + * no effects if the light does not cast shadows. This prevents artifacts from + * objects near to the light. It behaves like Lens::set_near_plane. + * + * It can also help increasing shadow map precision, low near planes will + * cause the precision to suffer. Try setting the near plane as big as possible. + * + * If a negative or zero near plane is passed, an assertion is thrown. + * + * @param near_plane Near-plane + */ +inline void RPLight::set_near_plane(float near_plane) { + nassertv(near_plane > 0.00001); + _near_plane = near_plane; + invalidate_shadows(); +} + +/** + * @brief Returns the near plane of the light + * @details This returns the lights near plane, previously set with + * RPLight::set_near_plane. If the light does not cast shadows, this value + * is meaningless. + * + * @return Near-plane + */ +inline float RPLight::get_near_plane() const { + return _near_plane; +} + diff --git a/contrib/src/rplight/rpLight.cxx b/contrib/src/rplight/rpLight.cxx new file mode 100644 index 0000000000..1dcb92a3d0 --- /dev/null +++ b/contrib/src/rplight/rpLight.cxx @@ -0,0 +1,137 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "rpLight.h" + + +/** + * @brief Constructs a new light with the given type + * @details This constructs a new base light with the given light type. + * Sub-Classes should call this to initialize all properties. + * + * @param light_type Type of the light + */ +RPLight::RPLight(LightType light_type) { + _light_type = light_type; + _needs_update = false; + _casts_shadows = false; + _slot = -1; + _position.fill(0); + _color.fill(1); + _ies_profile = -1; + _source_resolution = 512; + _near_plane = 0.5; + _energy = 20.0; +} + +/** + * @brief Writes the light to a GPUCommand + * @details This writes all of the lights data to the given GPUCommand handle. + * Subclasses should first call this method, and then append their own + * data. This makes sure that for unpacking a light, no information about + * the type of the light is required. + * + * @param cmd The GPUCommand to write to + */ +void RPLight::write_to_command(GPUCommand &cmd) { + cmd.push_int(_light_type); + cmd.push_int(_ies_profile); + + if (_casts_shadows) { + // If we casts shadows, write the index of the first source, we expect + // them to be consecutive + nassertv(_shadow_sources.size() >= 0); + nassertv(_shadow_sources[0]->has_slot()); + cmd.push_int(_shadow_sources[0]->get_slot()); + } else { + // If we cast no shadows, just push a negative number + cmd.push_int(-1); + } + + cmd.push_vec3(_position); + + // Get the lights color by multiplying color with energy. Divide by + // 100, since 16bit floating point buffers only go up to 65000.0, which + // prevents very bright lights + cmd.push_vec3(_color * _energy / 100.0); +} + +/** + * @brief Light destructor + * @details This destructs the light, cleaning up all resourced used. The light + * should be detached at this point, because while the Light is attached, + * the InternalLightManager holds a reference to prevent it from being + * destructed. + */ +RPLight::~RPLight() { + nassertv(!has_slot()); // Light still attached - should never happen + clear_shadow_sources(); +} + +/** + * @brief Sets the lights color from a given color temperature + * @details This sets the lights color, given a temperature. This is more + * physically based than setting a user defined color. The color will be + * computed from the given temperature. + * + * @param temperature Light temperature + */ +void RPLight::set_color_from_temperature(float temperature) { + + // Thanks to rdb for this conversion script + float mm = 1000.0 / temperature; + float mm2 = mm * mm; + float mm3 = mm2 * mm; + float x, y; + + if (temperature < 4000) { + x = -0.2661239 * mm3 - 0.2343580 * mm2 + 0.8776956 * mm + 0.179910; + } else { + x = -3.0258469 * mm3 + 2.1070379 * mm2 + 0.2226347 * mm + 0.240390; + } + + float x2 = x * x; + float x3 = x2 * x; + if (temperature < 2222) { + y = -1.1063814 * x3 - 1.34811020 * x2 + 2.18555832 * x - 0.20219683; + } else if (temperature < 4000) { + y = -0.9549476 * x3 - 1.37418593 * x2 + 2.09137015 * x - 0.16748867; + } else { + y = 3.0817580 * x3 - 5.87338670 * x2 + 3.75112997 * x - 0.37001483; + } + + // xyY to XYZ, assuming Y=1. + LVecBase3 xyz(x / y, 1, (1 - x - y) / y); + + // Convert XYZ to linearized sRGB. + const static LMatrix3 xyz_to_rgb( + 3.2406, -0.9689, 0.0557, + -1.5372, 1.8758, -0.2050, + -0.4986, 0.0415, 1.0570); + + set_color(xyz_to_rgb.xform(xyz)); +} diff --git a/contrib/src/rplight/rpLight.h b/contrib/src/rplight/rpLight.h new file mode 100644 index 0000000000..13665c23ad --- /dev/null +++ b/contrib/src/rplight/rpLight.h @@ -0,0 +1,130 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef RPLIGHT_H +#define RPLIGHT_H + +#include "referenceCount.h" +#include "luse.h" +#include "gpuCommand.h" +#include "shadowSource.h" + +/** + * @brief Base class for Lights + * @details This is the base class for all lights in the render pipeline. It + * stores common properties, and provides methods to modify these. + * It also defines some interface functions which subclasses have to implement. + */ +class RPLight : public ReferenceCount { +PUBLISHED: + /** + * Different types of light. + */ + enum LightType { + LT_empty = 0, + LT_point_light = 1, + LT_spot_light = 2, + }; + +public: + RPLight(LightType light_type); + virtual ~RPLight(); + + virtual void init_shadow_sources() = 0; + virtual void update_shadow_sources() = 0; + virtual void write_to_command(GPUCommand &cmd); + + inline int get_num_shadow_sources() const; + inline ShadowSource* get_shadow_source(size_t index) const; + inline void clear_shadow_sources(); + + inline void set_needs_update(bool flag); + inline bool get_needs_update() const; + + inline bool has_slot() const; + inline int get_slot() const; + inline void remove_slot(); + inline void assign_slot(int slot); + +PUBLISHED: + inline void invalidate_shadows(); + + inline void set_pos(const LVecBase3 &pos); + inline void set_pos(float x, float y, float z); + inline const LVecBase3& get_pos() const; + MAKE_PROPERTY(pos, get_pos, set_pos); + + inline void set_color(const LVecBase3 &color); + inline void set_color(float r, float g, float b); + inline const LVecBase3& get_color() const; + MAKE_PROPERTY(color, get_color, set_color); + + void set_color_from_temperature(float temperature); + + inline void set_energy(float energy); + inline float get_energy() const; + MAKE_PROPERTY(energy, get_energy, set_energy); + + inline LightType get_light_type() const; + MAKE_PROPERTY(light_type, get_light_type); + + inline void set_casts_shadows(bool flag = true); + inline bool get_casts_shadows() const; + MAKE_PROPERTY(casts_shadows, get_casts_shadows, set_casts_shadows); + + inline void set_shadow_map_resolution(size_t resolution); + inline size_t get_shadow_map_resolution() const; + MAKE_PROPERTY(shadow_map_resolution, get_shadow_map_resolution, set_shadow_map_resolution); + + inline void set_ies_profile(int profile); + inline int get_ies_profile() const; + inline bool has_ies_profile() const; + inline void clear_ies_profile(); + MAKE_PROPERTY2(ies_profile, has_ies_profile, get_ies_profile, + set_ies_profile, clear_ies_profile); + + inline void set_near_plane(float near_plane); + inline float get_near_plane() const; + MAKE_PROPERTY(near_plane, get_near_plane, set_near_plane); + +protected: + int _slot; + int _ies_profile; + size_t _source_resolution; + bool _needs_update; + bool _casts_shadows; + LVecBase3 _position; + LVecBase3 _color; + float _energy; + LightType _light_type; + float _near_plane; + + vector _shadow_sources; +}; + +#include "rpLight.I" + +#endif // RP_LIGHT_H diff --git a/contrib/src/rplight/rpPointLight.I b/contrib/src/rplight/rpPointLight.I new file mode 100644 index 0000000000..9daf29d598 --- /dev/null +++ b/contrib/src/rplight/rpPointLight.I @@ -0,0 +1,82 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +/** + * @brief Sets the radius of the light + * @details This sets the radius of the light. It controls the lights + * influence. After a distance greater than this radius, the light influence + * is zero. + * + * @param radius Light radius in world space + */ +inline void RPPointLight::set_radius(float radius) { + nassertv(radius > 0); // Invalid light radius + _radius = radius; + set_needs_update(true); + invalidate_shadows(); +} + +/** + * @brief Returns the lights radius + * @details This returns the lights radius previously set with + * RPPointLight::set_radius + * @return Light radius in world space + */ +inline float RPPointLight::get_radius() const { + return _radius; +} + +/** + * @brief Sets the inner radius of the light + * @details This sets the inner radius of the light. Anything greater than + * zero causes the light to get an area light. This has influence on the + * specular highlights of the light aswell as the shadows. + * + * The inner radius controls the size of the lights sphere size in world + * space units. A radius of 0 means the light has no inner radius, and the + * light will be have like an infinite small point light source. + * A radius greater than zero will cause the light to behave like it would be + * an emissive sphere with the given inner radius emitting light. This is + * more physically correct. + * + * @param inner_radius Inner-radius in world space + */ +inline void RPPointLight::set_inner_radius(float inner_radius) { + nassertv(inner_radius >= 0.01); // Invalid inner radius + _inner_radius = inner_radius; + set_needs_update(true); +} + +/** + * @brief Returns the inner radius of the light + * @details This returns the inner radius of the light, previously set with + * RPPointLight::get_inner_radius. + * @return [description] + */ +inline float RPPointLight::get_inner_radius() const { + return _inner_radius; +} diff --git a/contrib/src/rplight/rpPointLight.cxx b/contrib/src/rplight/rpPointLight.cxx new file mode 100644 index 0000000000..f4f0551d8f --- /dev/null +++ b/contrib/src/rplight/rpPointLight.cxx @@ -0,0 +1,90 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "rpPointLight.h" + + +/** + * @brief Constructs a new point light + * @details This contructs a new point light with default settings. By default + * the light is set to be an infinitely small point light source. You can + * change this with RPPointLight::set_inner_radius. + */ +RPPointLight::RPPointLight() : RPLight(RPLight::LT_point_light) { + _radius = 10.0; + _inner_radius = 0.01; +} + +/** + * @brief Writes the light to a GPUCommand + * @details This writes the point light data to a GPUCommand. + * @see RPLight::write_to_command + * + * @param cmd The target GPUCommand + */ +void RPPointLight::write_to_command(GPUCommand &cmd) { + RPLight::write_to_command(cmd); + cmd.push_float(_radius); + cmd.push_float(_inner_radius); +} + +/** + * @brief Inits the shadow sources of the light + * @details This inits all required shadow sources for the point light. + * @see RPLight::init_shadow_sources + */ +void RPPointLight::init_shadow_sources() { + nassertv(_shadow_sources.size() == 0); + // Create 6 shadow sources, one for each direction + for(size_t i = 0; i < 6; ++i) { + _shadow_sources.push_back(new ShadowSource()); + } +} + +/** + * @brief Updates the shadow sources + * @details This updates all shadow sources of the light. + * @see RPLight::update_shadow_sources + */ +void RPPointLight::update_shadow_sources() { + LVecBase3 directions[6] = { + LVecBase3( 1, 0, 0), + LVecBase3(-1, 0, 0), + LVecBase3( 0, 1, 0), + LVecBase3( 0, -1, 0), + LVecBase3( 0, 0, 1), + LVecBase3( 0, 0, -1) + }; + + // Increase fov to prevent artifacts at the shadow map transitions + const float fov = 90.0f + 3.0f; + for (size_t i = 0; i < _shadow_sources.size(); ++i) { + _shadow_sources[i]->set_resolution(get_shadow_map_resolution()); + _shadow_sources[i]->set_perspective_lens(fov, _near_plane, _radius, + _position, directions[i]); + } +} diff --git a/contrib/src/rplight/rpPointLight.h b/contrib/src/rplight/rpPointLight.h new file mode 100644 index 0000000000..3d933ef266 --- /dev/null +++ b/contrib/src/rplight/rpPointLight.h @@ -0,0 +1,63 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef RPPOINTLIGHT_H +#define RPPOINTLIGHT_H + +#include "pandabase.h" +#include "rpLight.h" + +/** + * @brief PointLight class + * @details This represents a point light, a light which has a position and + * radius. Checkout the RenderPipeline documentation for more information + * about this type of light. + */ +class RPPointLight : public RPLight { +PUBLISHED: + RPPointLight(); + + inline void set_radius(float radius); + inline float get_radius() const; + MAKE_PROPERTY(radius, get_radius, set_radius); + + inline void set_inner_radius(float inner_radius); + inline float get_inner_radius() const; + MAKE_PROPERTY(inner_radius, get_inner_radius, set_inner_radius); + +public: + virtual void write_to_command(GPUCommand &cmd); + virtual void update_shadow_sources(); + virtual void init_shadow_sources(); + +protected: + float _radius; + float _inner_radius; +}; + +#include "rpPointLight.I" + +#endif // RPPOINTLIGHT_H diff --git a/contrib/src/rplight/rpSpotLight.I b/contrib/src/rplight/rpSpotLight.I new file mode 100644 index 0000000000..d1edf8d30d --- /dev/null +++ b/contrib/src/rplight/rpSpotLight.I @@ -0,0 +1,74 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + + +inline void RPSpotLight::set_radius(float radius) { + _radius = radius; + set_needs_update(true); + invalidate_shadows(); +} + +inline float RPSpotLight::get_radius() const { + return _radius; +} + + +inline void RPSpotLight::set_fov(float fov) { + _fov = fov; + set_needs_update(true); + invalidate_shadows(); +} + +inline float RPSpotLight::get_fov() const { + return _fov; +} + +inline void RPSpotLight::set_direction(LVecBase3 direction) { + _direction = direction; + _direction.normalize(); + set_needs_update(true); + invalidate_shadows(); +} + +inline void RPSpotLight::set_direction(float dx, float dy, float dz) { + _direction.set(dx, dy, dz); + _direction.normalize(); + set_needs_update(true); + invalidate_shadows(); +} + +inline const LVecBase3& RPSpotLight::get_direction() const { + return _direction; +} + +inline void RPSpotLight::look_at(LVecBase3 point) { + set_direction(point - _position); +} + +inline void RPSpotLight::look_at(float x, float y, float z) { + set_direction(LVecBase3(x, y, z) - _position); +} diff --git a/contrib/src/rplight/rpSpotLight.cxx b/contrib/src/rplight/rpSpotLight.cxx new file mode 100644 index 0000000000..649e0b1beb --- /dev/null +++ b/contrib/src/rplight/rpSpotLight.cxx @@ -0,0 +1,80 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "rpSpotLight.h" + +#define _USE_MATH_DEFINES +#include + + +/** + * @brief Creates a new spot light + * @details This creates a new spot light with default properties set. You should + * set at least a direction, fov, radius and position to make the light useful. + */ +RPSpotLight::RPSpotLight() : RPLight(RPLight::LT_spot_light) { + _radius = 10.0; + _fov = 45.0; + _direction.set(0, 0, -1); +} + +/** + * @brief Writes the light to a GPUCommand + * @details This writes the spot light data to a GPUCommand. + * @see RPLight::write_to_command + * + * @param cmd The target GPUCommand + */ +void RPSpotLight::write_to_command(GPUCommand &cmd) { + RPLight::write_to_command(cmd); + cmd.push_float(_radius); + + // Encode FOV as cos(fov) + cmd.push_float(cos(_fov / 360.0 * M_PI)); + cmd.push_vec3(_direction); +} + +/** + * @brief Inits the shadow sources of the light + * @details This inits all required shadow sources for the spot light. + * @see RPLight::init_shadow_sources + */ +void RPSpotLight::init_shadow_sources() { + nassertv(_shadow_sources.size() == 0); + _shadow_sources.push_back(new ShadowSource()); +} + +/** + * @brief Updates the shadow sources + * @details This updates all shadow sources of the light. + * @see RPLight::update_shadow_sources + */ +void RPSpotLight::update_shadow_sources() { + _shadow_sources[0]->set_resolution(get_shadow_map_resolution()); + _shadow_sources[0]->set_perspective_lens(_fov, _near_plane, _radius, _position, _direction); +} + diff --git a/contrib/src/rplight/rpSpotLight.h b/contrib/src/rplight/rpSpotLight.h new file mode 100644 index 0000000000..d27e226950 --- /dev/null +++ b/contrib/src/rplight/rpSpotLight.h @@ -0,0 +1,71 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef RPSPOTLIGHT_H +#define RPSPOTLIGHT_H + +#include "pandabase.h" +#include "rpLight.h" + +/** + * @brief SpotLight class + * @details This represents a spot light, a light which has a position, radius, + * direction and FoV. Checkout the RenderPipeline documentation for more + * information about this type of light. + */ +class RPSpotLight : public RPLight { +PUBLISHED: + RPSpotLight(); + + inline void set_radius(float radius); + inline float get_radius() const; + MAKE_PROPERTY(radius, get_radius, set_radius); + + inline void set_fov(float fov); + inline float get_fov() const; + MAKE_PROPERTY(fov, get_fov, set_fov); + + inline void set_direction(LVecBase3 direction); + inline void set_direction(float dx, float dy, float dz); + inline const LVecBase3& get_direction() const; + inline void look_at(LVecBase3 point); + inline void look_at(float x, float y, float z); + MAKE_PROPERTY(direction, get_direction, set_direction); + +public: + virtual void write_to_command(GPUCommand &cmd); + virtual void init_shadow_sources(); + virtual void update_shadow_sources(); + +protected: + float _radius; + float _fov; + LVecBase3 _direction; +}; + +#include "rpSpotLight.I" + +#endif // RPSPOTLIGHT_H diff --git a/contrib/src/rplight/shadowAtlas.I b/contrib/src/rplight/shadowAtlas.I new file mode 100644 index 0000000000..f48fc451f1 --- /dev/null +++ b/contrib/src/rplight/shadowAtlas.I @@ -0,0 +1,159 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +/** + * @brief Returns the tile size of the atlas. + * @details This returns the tile size of the atlas, which was set in the + * constructor. This is the smalles unit of the atlas, and every resolution + * has to be a multiple of the tile size. + * @return Tile size in pixels + */ +inline int ShadowAtlas::get_tile_size() const { + return _tile_size; +} + +/** + * @brief Sets a specific tile status. + * @details This marks a tile to either reserved or free, depending on the flag. + * If flag is true, the tile gets marked as reserved. If flag is false, the + * tile gets marked as free. + * + * No bounds checking is done for performance reasons. Passing an invalid tile + * index causes a crash. The coordinates are expected to be in tile space. + * + * @param x x-position of the tile + * @param y y-position of the tile + * @param flag Flag to set the tile to + */ +inline void ShadowAtlas::set_tile(size_t x, size_t y, bool flag) { + _flags[x + y * _num_tiles] = flag; +} + +/** + * @brief Returns the status of a given tile. + * @details This returns the value of a tile. If the tile is true, this means + * the tile is already reserved. If the tile is false, the tile can be + * used, and is not reserved. + * + * No bounds checking is done for performance reasons. Passing an invalid tile + * index causes a crash. The coordinates are expected to be in tile space. + * + * @param x x-position of the tile + * @param y y-position of the tile + * + * @return Tile-Status + */ +inline bool ShadowAtlas::get_tile(size_t x, size_t y) const { + return _flags[x + y * _num_tiles]; +} + +/** + * @brief Checks wheter a given region is free. + * @details This checks whether a given region in the atlas is still free. This + * is true if *all* tiles in that region are false, and thus are not taken yet. + * The coordinates are expected to be in tile space. + * + * Passing an invalid region, causes an assertion, in case those are enabled. + * If assertions are optimized out, this method will crash when passing invalid + * bounds. + * + * @param x x- start position of the region + * @param y y- start position of the region + * @param w width of the region + * @param h height of the region + * @return true if the region is completely free, else false + */ +inline bool ShadowAtlas::region_is_free(size_t x, size_t y, size_t w, size_t h) const { + // Check if we are out of bounds, this should be disabled for performance + // reasons at some point. + nassertr(x >= 0 && y >= 0 && x + w <= _num_tiles && y + h <= _num_tiles, false); + + // Iterate over every tile in that region and check if it is still free. + for (size_t cx = 0; cx < w; ++cx) { + for (size_t cy = 0; cy < h; ++cy) { + if (get_tile(cx + x, cy + y)) return false; + } + } + return true; +} + +/** + * @brief Returns the amount of tiles required to store a resolution. + * @details Returns the amount of tiles which would be required to store a + * given resolution. This basically just returns resolution / tile_size. + * + * When an invalid resolution is passed (not a multiple of the tile size), + * an error is printed and 1 is returned. + * When a negative or zero resolution is passed, undefined behaviour occurs. + * + * @param resolution The resolution to compute the amount of tiles for + * @return Amount of tiles to store the resolution + */ +inline int ShadowAtlas::get_required_tiles(size_t resolution) const { + nassertr(resolution > 0, -1); + + if (resolution % _tile_size != 0) { + shadowatlas_cat.error() << "Resolution " << resolution << " is not a multiple " + << "of the shadow atlas tile size (" << _tile_size << ")!" << endl; + return 1; + } + return resolution / _tile_size; +} + +/** + * @brief Converts a tile-space region to uv space. + * @details This converts a region (presumably from ShadowAtlas::find_and_reserve_region) + * to uv space (0 .. 1 range). This can be used in shaders, since they expect + * floating point coordinates instead of integer coordinates. + * + * @param region tile-space region + * @return uv-space region + */ +inline LVecBase4 ShadowAtlas::region_to_uv(const LVecBase4i& region) { + LVecBase4 flt = LVecBase4(region.get_x(), region.get_y(), region.get_z(), region.get_w()); + return flt * ((float)_tile_size / (float)_size); +} + +/** + * @brief Returns the amount of used tiles + * @details Returns the amount of used tiles in the atlas + * @return Amount of used tiles + */ +inline int ShadowAtlas::get_num_used_tiles() const { + return _num_used_tiles; +} + +/** + * @brief Returns the amount of used tiles in percentage + * @details This returns in percentage from 0 to 1 how much space of the atlas + * is used right now. A value of 1 means the atlas is completely full, whereas + * a value of 0 means the atlas is completely free. + * @return Atlas usage in percentage + */ +inline float ShadowAtlas::get_coverage() const { + return float(_num_used_tiles) / float(_num_tiles * _num_tiles); +} diff --git a/contrib/src/rplight/shadowAtlas.cxx b/contrib/src/rplight/shadowAtlas.cxx new file mode 100644 index 0000000000..15d4ea1bd0 --- /dev/null +++ b/contrib/src/rplight/shadowAtlas.cxx @@ -0,0 +1,186 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "shadowAtlas.h" +#include + +NotifyCategoryDef(shadowatlas, ""); + +/** + * @brief Constructs a new shadow atlas. + * @details This constructs a new shadow atlas with the given size and tile size. + * + * The size determines the total size of the atlas in pixels. It should be a + * power-of-two to favour the GPU. + * + * The tile_size determines the smallest unit of tiles the atlas can store. + * If, for example, a tile_size of 32 is used, then every entry stored must + * have a resolution of 32 or greater, and the resolution must be a multiple + * of 32. This is to optimize the search in the atlas, so the atlas does not + * have to check every pixel, and instead can just check whole tiles. + * + * If you want to disable the use of tiles, set the tile_size to 1, which + * will make the shadow atlas use pixels instead of tiles. + * + * @param size Atlas-size in pixels + * @param tile_size tile-size in pixels, or 1 to use no tiles. + */ +ShadowAtlas::ShadowAtlas(size_t size, size_t tile_size) { + nassertv(size > 1 && tile_size >= 1); + nassertv(tile_size < size && size % tile_size == 0); + _size = size; + _tile_size = tile_size; + _num_used_tiles = 0; + init_tiles(); +} + +/** + * @brief Destructs the shadow atlas. + * @details This destructs the shadow atlas, freeing all used resources. + */ +ShadowAtlas::~ShadowAtlas() { + delete [] _flags; +} + +/** + * @brief Internal method to init the storage. + * @details This method setups the storage used for storing the tile flags. + */ +void ShadowAtlas::init_tiles() { + _num_tiles = _size / _tile_size; + _flags = new bool[_num_tiles * _num_tiles]; + memset(_flags, 0x0, sizeof(bool) * _num_tiles * _num_tiles); +} + +/** + * @brief Internal method to reserve a region in the atlas. + * @details This reserves a given region in the shadow atlas. The region should + * be in tile space.This is called by the ShadowAtlas::find_and_reserve_region. + * It sets all flags in that region to true, indicating that those are used. + * When an invalid region is passed, an assertion is triggered. If assertions + * are optimized out, undefined behaviour occurs. + * + * @param x x- start positition of the region + * @param y y- start position of the region + * @param w width of the region + * @param h height of the region + */ +void ShadowAtlas::reserve_region(size_t x, size_t y, size_t w, size_t h) { + // Check if we are out of bounds, this should be disabled for performance + // reasons at some point. + nassertv(x >= 0 && y >= 0 && x + w <= _num_tiles && y + h <= _num_tiles); + + _num_used_tiles += w * h; + + // Iterate over every tile in the region and mark it as used + for (size_t cx = 0; cx < w; ++cx) { + for (size_t cy = 0; cy < h; ++cy) { + set_tile(cx + x, cy + y, true); + } + } +} + +/** + * @brief Finds space for a map of the given size in the atlas. + * @details This methods searches for a space to store a region of the given + * size in the atlas. tile_width and tile_height should be already in tile + * space. They can be converted using ShadowAtlas::get_required_tiles. + * + * If no region is found, or an invalid size is passed, an integer vector with + * all components set to -1 is returned. + * + * If a region is found, an integer vector with the given layout is returned: + * x: x- Start of the region + * y: y- Start of the region + * z: width of the region + * w: height of the region + * + * The layout is in tile space, and can get converted to uv space using + * ShadowAtlas::region_to_uv. + * + * @param tile_width Width of the region in tile space + * @param tile_height Height of the region in tile space + * + * @return Region, see description, or -1 when no region is found. + */ +LVecBase4i ShadowAtlas::find_and_reserve_region(size_t tile_width, size_t tile_height) { + + // Check for empty region + if (tile_width < 1 || tile_height < 1) { + shadowatlas_cat.error() << "Called find_and_reserve_region with null-region!" << endl; + return LVecBase4i(-1); + } + + // Check for region bigger than the shadow atlas + if (tile_width > _num_tiles || tile_height > _num_tiles) { + shadowatlas_cat.error() << "Requested region exceeds shadow atlas size!" << endl; + return LVecBase4i(-1); + } + + // Iterate over every possible region and check if its still free + for (size_t x = 0; x <= _num_tiles - tile_width; ++x) { + for (size_t y = 0; y <= _num_tiles - tile_height; ++y) { + if (region_is_free(x, y, tile_width, tile_height)) { + // Found free region, now reserve it + reserve_region(x, y, tile_width, tile_height); + return LVecBase4i(x, y, tile_width, tile_height); + } + } + } + + // When we reached this part, we couldn't find a free region, so the atlas + // seems to be full. + shadowatlas_cat.error() << "Failed to find a free region of size " << tile_width + << " x " << tile_height << "!" << endl; + return LVecBase4i(-1); +} + +/** + * @brief Frees a given region + * @details This frees a given region, marking it as free so that other shadow + * maps can use the space again. The region should be the same as returned + * by ShadowAtlas::find_and_reserve_region. + * + * If an invalid region is passed, an assertion is triggered. If assertions + * are compiled out, undefined behaviour will occur. + * + * @param region Region to free + */ +void ShadowAtlas::free_region(const LVecBase4i& region) { + // Out of bounds check, can't hurt + nassertv(region.get_x() >= 0 && region.get_y() >= 0); + nassertv(region.get_x() + region.get_z() <= _num_tiles && region.get_y() + region.get_w() <= _num_tiles); + + _num_used_tiles -= region.get_z() * region.get_w(); + + for (size_t x = 0; x < region.get_z(); ++x) { + for (size_t y = 0; y < region.get_w(); ++y) { + // Could do an assert here, that the tile should have been used (=true) before + set_tile(region.get_x() + x, region.get_y() + y, false); + } + } +} diff --git a/contrib/src/rplight/shadowAtlas.h b/contrib/src/rplight/shadowAtlas.h new file mode 100644 index 0000000000..e3f0958a50 --- /dev/null +++ b/contrib/src/rplight/shadowAtlas.h @@ -0,0 +1,80 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef SHADOWATLAS_H +#define SHADOWATLAS_H + +#include "pandabase.h" +#include "lvecBase4.h" + +NotifyCategoryDecl(shadowatlas, EXPORT_CLASS, EXPORT_TEMPL); + + +/** + * @brief Class which manages distributing shadow maps in an atlas. + * @details This class manages the shadow atlas. It handles finding and reserving + * space for new shadow maps. + */ +class ShadowAtlas { +PUBLISHED: + ShadowAtlas(size_t size, size_t tile_size = 32); + ~ShadowAtlas(); + + inline int get_num_used_tiles() const; + inline float get_coverage() const; + + MAKE_PROPERTY(num_used_tiles, get_num_used_tiles); + MAKE_PROPERTY(coverage, get_coverage); + +public: + + LVecBase4i find_and_reserve_region(size_t tile_width, size_t tile_height); + void free_region(const LVecBase4i& region); + inline LVecBase4 region_to_uv(const LVecBase4i& region); + + inline int get_tile_size() const; + inline int get_required_tiles(size_t resolution) const; + +protected: + + void init_tiles(); + + inline void set_tile(size_t x, size_t y, bool flag); + inline bool get_tile(size_t x, size_t y) const; + + inline bool region_is_free(size_t x, size_t y, size_t w, size_t h) const; + void reserve_region(size_t x, size_t y, size_t w, size_t h); + + size_t _size; + size_t _num_tiles; + size_t _tile_size; + size_t _num_used_tiles; + bool* _flags; +}; + +#include "shadowAtlas.I" + +#endif // SHADOWATLAS_H diff --git a/contrib/src/rplight/shadowManager.I b/contrib/src/rplight/shadowManager.I new file mode 100644 index 0000000000..f5d87c463e --- /dev/null +++ b/contrib/src/rplight/shadowManager.I @@ -0,0 +1,192 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +/** + * @brief Sets the maximum amount of updates per frame. + * @details This controls the maximum amount of updated ShadowSources per frame. + * The ShadowManager will take the first ShadowSources, and + * generate shadow maps for them every frame. If there are more ShadowSources + * waiting to get updated than available updates, the sources are sorted by + * priority, and the update of the less important sources is delayed to the + * next frame. + * + * If the update count is set too low, and there are a lot of ShadowSources + * waiting to get updated, artifacts will occur, and there might be ShadowSources + * which never get updated, due to low priority. + * + * If an update count of 0 is passed, no updates will happen. This also means + * that there are no shadows. This is not recommended. + * + * If an update count < 0 is passed, undefined behaviour occurs. + * + * This method has to get called before ShadowManager::init, otherwise an + * assertion will get triggered. + * + * @param max_updates Maximum amoumt of updates + */ +inline void ShadowManager::set_max_updates(size_t max_updates) { + nassertv(max_updates >= 0); + nassertv(_atlas == nullptr); // ShadowManager was already initialized + if (max_updates == 0) { + shadowmanager_cat.warning() << "max_updates set to 0, no shadows will be updated." << endl; + } + _max_updates = max_updates; +} + +/** + * @brief Sets the shadow atlas size + * @details This sets the desired shadow atlas size. It should be big enough + * to store all important shadow sources, with some buffer, because the shadow + * maps usually won't be fitted perfectly, so gaps can occur. + * + * This has to get called before calling ShadowManager::init. When calling this + * method after initialization, an assertion will get triggered. + * + * @param atlas_size Size of the shadow atlas in pixels + */ +inline void ShadowManager::set_atlas_size(size_t atlas_size) { + nassertv(atlas_size >= 16 && atlas_size <= 16384); + nassertv(_atlas == nullptr); // ShadowManager was already initialized + _atlas_size = atlas_size; +} + +/** + * @brief Returns the shadow atlas size. + * @details This returns the shadow atlas size previously set with + * ShadowManager::set_atlas_size. + * @return Shadow atlas size in pixels + */ +inline size_t ShadowManager::get_atlas_size() const { + return _atlas_size; +} + + +/** + * @brief Returns a handle to the shadow atlas. + * @details This returns a handle to the internal shadow atlas instance. This + * is only valid after calling ShadowManager::init. Calling this earlier will + * trigger an assertion and undefined behaviour. + * @return The internal ShadowAtlas instance + */ +inline ShadowAtlas* ShadowManager::get_atlas() const { + nassertr(_atlas != nullptr, nullptr); // Can't hurt to check + return _atlas; +} + +/** + * @brief Sets the target scene + * @details This sets the target scene for rendering shadows. All shadow cameras + * will be parented to this scene to render shadows. + * + * Usually the scene will be ShowBase.render. If the scene is an empty or + * invalid NodePath, an assertion will be triggered. + * + * This method has to get called before calling ShadowManager::init, or an + * assertion will get triggered. + * + * @param scene_parent The target scene + */ +inline void ShadowManager::set_scene(NodePath scene_parent) { + nassertv(!scene_parent.is_empty()); + nassertv(_atlas == nullptr); // ShadowManager was already initialized + _scene_parent = scene_parent; +} + +/** + * @brief Sets the handle to the TagStageManager. + * @details This sets the handle to the TagStateManager used by the pipeline. + * Usually this is RenderPipeline.get_tag_mgr(). + * + * This has to get called before ShadowManager::init, otherwise an assertion + * will get triggered. + * + * @param tag_mgr [description] + */ +inline void ShadowManager::set_tag_state_manager(TagStateManager* tag_mgr) { + nassertv(tag_mgr != nullptr); + nassertv(_atlas == nullptr); // ShadowManager was already initialized + _tag_state_mgr = tag_mgr; +} + +/** + * @brief Sets the handle to the Shadow targets output + * @details This sets the handle to the GraphicsOutput of the shadow atlas. + * Usually this is RenderTarget.get_internal_buffer(), whereas the RenderTarget + * is the target of the ShadowStage. + * + * This is used for creating display regions and attaching cameras to them, + * for performing shadow updates. + * + * This has to get called before ShadowManager::init, otherwise an assertion + * will be triggered. + * + * @param graphics_output [description] + */ +inline void ShadowManager::set_atlas_graphics_output(GraphicsOutput* graphics_output) { + nassertv(graphics_output != nullptr); + nassertv(_atlas == nullptr); // ShadowManager was already initialized + _atlas_graphics_output = graphics_output; +} + + +/** + * @brief Adds a new shadow update + * @details This adds a new update to the update queue. When the queue is already + * full, this method returns false, otherwise it returns true. The next time + * the manager is updated, the shadow source will recieve an update of its + * shadow map. + * + * @param source The shadow source to update + * + * @return Whether the shadow source udpate was sucessfully queued. + */ +inline bool ShadowManager::add_update(const ShadowSource* source) { + nassertr(_atlas != nullptr, false); // ShadowManager::init not called yet. + nassertr(source != nullptr, false); // nullptr-Pointer passed + + if (_queued_updates.size() >= _max_updates) { + if (shadowmanager_cat.is_debug()) { + shadowmanager_cat.debug() << "cannot update source, out of update slots" << endl; + } + return false; + } + + // Add the update to the queue + _queued_updates.push_back(source); + return true; +} + +/** + * @brief Returns how many update slots are left. + * @details This returns how many update slots are left. You can assume the + * next n calls to add_update will succeed, whereas n is the value returned + * by this function. + * @return Number of update slots left. + */ +inline size_t ShadowManager::get_num_update_slots_left() const { + return _max_updates - _queued_updates.size(); +} diff --git a/contrib/src/rplight/shadowManager.cxx b/contrib/src/rplight/shadowManager.cxx new file mode 100644 index 0000000000..8b7b1b1f96 --- /dev/null +++ b/contrib/src/rplight/shadowManager.cxx @@ -0,0 +1,157 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "shadowManager.h" + +NotifyCategoryDef(shadowmanager, ""); + +/** + * @brief Constructs a new shadow atlas + * @details This constructs a new shadow atlas. There are a set of properties + * which should be set before calling ShadowManager::init, see the set-Methods. + * After all properties are set, ShadowManager::init should get called. + * ShadowManager::update should get called on a per frame basis. + */ +ShadowManager::ShadowManager() { + _max_updates = 10; + _atlas = nullptr; + _atlas_size = 4096; + _tag_state_mgr = nullptr; + _atlas_graphics_output = nullptr; +} + +/** + * @brief Destructs the ShadowManager + * @details This destructs the shadow manager, clearing all resources used + */ +ShadowManager::~ShadowManager() { + delete _atlas; + + // Todo: Could eventually unregister all shadow cameras. Since the tag state + // manager does this on cleanup already, and we get destructed at the same + // time (if at all), this is not really necessary +} + + +/** + * @brief Initializes the ShadowManager. + * @details This initializes the ShadowManager. All properties should have + * been set before calling this, otherwise assertions will get triggered. + * + * This setups everything required for rendering shadows, including the + * shadow atlas and the various shadow cameras. After calling this method, + * no properties can be changed anymore. + */ +void ShadowManager::init() { + nassertv(!_scene_parent.is_empty()); // Scene parent not set, call set_scene_parent before init! + nassertv(_tag_state_mgr != nullptr); // TagStateManager not set, call set_tag_state_mgr before init! + nassertv(_atlas_graphics_output != nullptr); // AtlasGraphicsOutput not set, call set_atlas_graphics_output before init! + + _cameras.resize(_max_updates); + _display_regions.resize(_max_updates); + _camera_nps.reserve(_max_updates); + + // Create the cameras and regions + for(size_t i = 0; i < _max_updates; ++i) { + + // Create the camera + PT(Camera) camera = new Camera("ShadowCam-" + to_string((long long)i)); + camera->set_lens(new MatrixLens()); + camera->set_active(false); + camera->set_scene(_scene_parent); + _tag_state_mgr->register_camera("shadow", camera); + _camera_nps.push_back(_scene_parent.attach_new_node(camera)); + _cameras[i] = camera; + + // Create the display region + PT(DisplayRegion) region = _atlas_graphics_output->make_display_region(); + region->set_sort(1000); + region->set_clear_depth_active(true); + region->set_clear_depth(1.0); + region->set_clear_color_active(false); + region->set_camera(_camera_nps[i]); + region->set_active(false); + _display_regions[i] = region; + } + + // Create the atlas + _atlas = new ShadowAtlas(_atlas_size); + + // Reserve enough space for the updates + _queued_updates.reserve(_max_updates); +} + + +/** + * @brief Updates the ShadowManager + * @details This updates the ShadowManager, processing all shadow sources which + * need to get updated. + * + * This first collects all sources which require an update, sorts them by priority, + * and then processes the first ShadowSources. + * + * This may not get called before ShadowManager::init, or an assertion will be + * thrown. + */ +void ShadowManager::update() { + nassertv(_atlas != nullptr); // ShadowManager::init not called yet + nassertv(_queued_updates.size() <= _max_updates); // Internal error, should not happen + + // Disable all cameras and regions which will not be used + for (size_t i = _queued_updates.size(); i < _max_updates; ++i) { + _cameras[i]->set_active(false); + _display_regions[i]->set_active(false); + } + + // Iterate over all queued updates + for (size_t i = 0; i < _queued_updates.size(); ++i) { + const ShadowSource* source = _queued_updates[i]; + + // Enable the camera and display region, so they perform a render + _cameras[i]->set_active(true); + _display_regions[i]->set_active(true); + + // Set the view projection matrix + DCAST(MatrixLens, _cameras[i]->get_lens())->set_user_mat(source->get_mvp()); + + // Optional: Show the camera frustum for debugging + // _cameras[i]->show_frustum(); + + // Set the correct dimensions on the display region + const LVecBase4& uv = source->get_uv_region(); + _display_regions[i]->set_dimensions( + uv.get_x(), // left + uv.get_x() + uv.get_z(), // right + uv.get_y(), // bottom + uv.get_y() + uv.get_w() // top + ); + } + + // Clear the update list + _queued_updates.clear(); + _queued_updates.reserve(_max_updates); +} diff --git a/contrib/src/rplight/shadowManager.h b/contrib/src/rplight/shadowManager.h new file mode 100644 index 0000000000..279936b3bb --- /dev/null +++ b/contrib/src/rplight/shadowManager.h @@ -0,0 +1,91 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef SHADOWMANAGER_H +#define SHADOWMANAGER_H + +#include "pandabase.h" +#include "camera.h" +#include "luse.h" +#include "matrixLens.h" +#include "referenceCount.h" +#include "nodePath.h" +#include "displayRegion.h" +#include "graphicsOutput.h" + +#include "tagStateManager.h" +#include "shadowSource.h" +#include "shadowAtlas.h" + +NotifyCategoryDecl(shadowmanager, EXPORT_CLASS, EXPORT_TEMPL); + + +class ShadowManager : public ReferenceCount { +PUBLISHED: + ShadowManager(); + ~ShadowManager(); + + inline void set_max_updates(size_t max_updates); + inline void set_scene(NodePath scene_parent); + inline void set_tag_state_manager(TagStateManager* tag_mgr); + inline void set_atlas_graphics_output(GraphicsOutput* graphics_output); + + inline void set_atlas_size(size_t atlas_size); + inline size_t get_atlas_size() const; + MAKE_PROPERTY(atlas_size, get_atlas_size, set_atlas_size); + + inline size_t get_num_update_slots_left() const; + MAKE_PROPERTY(num_update_slots_left, get_num_update_slots_left); + + inline ShadowAtlas* get_atlas() const; + MAKE_PROPERTY(atlas, get_atlas); + + void init(); + void update(); + +public: + inline bool add_update(const ShadowSource* source); + +private: + size_t _max_updates; + size_t _atlas_size; + NodePath _scene_parent; + + pvector _cameras; + pvector _camera_nps; + pvector _display_regions; + + ShadowAtlas* _atlas; + TagStateManager* _tag_state_mgr; + GraphicsOutput* _atlas_graphics_output; + + typedef pvector UpdateQueue; + UpdateQueue _queued_updates; +}; + +#include "shadowManager.I" + +#endif // SHADOWMANAGER_H diff --git a/contrib/src/rplight/shadowSource.I b/contrib/src/rplight/shadowSource.I new file mode 100644 index 0000000000..e7ee9441b9 --- /dev/null +++ b/contrib/src/rplight/shadowSource.I @@ -0,0 +1,262 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + + +/** + * @brief Returns whether the shadow source needs an update. + * @details This returns the update flag, which was previously set with + * ShadowSource::set_needs_update. If the value is true, it means that the + * ShadowSource is invalid and should be regenerated. This can either be the + * case because the scene changed and affected the shadow source, or the light + * moved. + * @return Update-Flag + */ +inline bool ShadowSource::get_needs_update() const { + return !has_region() || _needs_update; +} + +/** + * @brief Returns the slot of the shadow source. + * @details This returns the assigned slot of the ShadowSource, or -1 if no slot + * was assigned yet. You can check if a slot exists with ShadowSource::has_slot. + * The slot is the index of the ShadowSource in the global source buffer. + * @return Slot, or -1 to indicate no slot. + */ +inline int ShadowSource::get_slot() const { + return _slot; +} + +/** + * @brief Returns whether the source has a slot. + * @details This returns whether the ShadowSource currently has an assigned slot. + * If the source has a slot assigned, this returns true, otherwise false. Cases + * where the source has no slot might be when the source just got attached, but + * never got rendered yet. + * @return [description] + */ +inline bool ShadowSource::has_slot() const { + return _slot >= 0; +} + +/** + * @brief Assigns the source a slot + * @details This assigns a slot to the ShadowSource. This is called from the + * ShadowManager, when the source gets attached first time. This should not + * get called by the user. + * + * @param slot Slot of the source, or -1 to indicate no slot. + */ +inline void ShadowSource::set_slot(int slot) { + _slot = slot; +} + +/** + * @brief Setups a perspective lens for the source. + * @details This makes the shadow source behave like a perspective lens. The + * parameters are similar to the ones of a PerspectiveLens. + * + * @param fov FoV of the lens + * @param near_plane The near plane of the lens, to avoid artifacts at low distance + * @param far_plane The far plane of the lens + * @param pos Position of the lens, in world space + * @param direction Direction (Orientation) of the lens + */ +inline void ShadowSource:: +set_perspective_lens(PN_stdfloat fov, PN_stdfloat near_plane, + PN_stdfloat far_plane, LVecBase3 pos, + LVecBase3 direction) { + // Construct the transfo*rmation matrix + LMatrix4 transform_mat = LMatrix4::translate_mat(-pos); + + // Construct a temporary lens to generate the lens matrix + PerspectiveLens temp_lens = PerspectiveLens(fov, fov); + temp_lens.set_film_offset(0, 0); + temp_lens.set_near_far(near_plane, far_plane); + temp_lens.set_view_vector(direction, LVector3::up()); + set_matrix_lens(transform_mat * temp_lens.get_projection_mat()); + + // Set new bounds, approximate with sphere + CPT(BoundingHexahedron) hexahedron = DCAST(BoundingHexahedron, temp_lens.make_bounds()); + LPoint3 center = (hexahedron->get_min() + hexahedron->get_max()) * 0.5f; + _bounds = BoundingSphere(pos + center, (hexahedron->get_max() - center).length()); +} + +/** + * @brief Sets a custom matrix for the source. + * @details This tells the source to use a custom matrix for rendering, just like + * the matrix lens. The matrix should include all transformations, rotations and + * scales. No other matrices will be used for rendering this shadow source (not + * even a coordinate system conversion). + * + * @param mvp Custom View-Projection matrix + */ +inline void ShadowSource::set_matrix_lens(const LMatrix4& mvp) { + _mvp = mvp; + set_needs_update(true); +} + +/** + * @brief Sets the update flag of the source. + * @details Sets whether the source is still valid, or needs to get regenerated. + * Usually you only want to flag the shadow source as invalid, by passing + * true as the flag. However, the ShadowManager will set the flag to false + * after updating the source. + * + * @param flag The update flag + */ +inline void ShadowSource::set_needs_update(bool flag) { + _needs_update = flag; +} + +/** + * @brief Returns whether the source has a valid region. + * @details This returns whether the ShadowSource has a valid shadow atlas region + * assigned. This might be not the case when the source never was rendered yet, + * or is about to get updated. + * @return true if the source has a valid region, else false. + */ +inline bool ShadowSource::has_region() const { + return _region.get_x() >= 0 && _region.get_y() >= 0 && _region.get_z() >= 0 && _region.get_w() >= 0; +} + +/** + * @brief Returns the resolution of the source. + * @details Returns the shadow map resolution of source, in pixels. This is the + * space the source takes in the shadow atlas, in pixels. + * @return Resolution in pixels + */ +inline size_t ShadowSource::get_resolution() const { + return _resolution; +} + +/** + * @brief Returns the assigned region of the source in atlas space. + * @details This returns the region of the source, in atlas space. This is the + * region set by ShadowSource::set_region. If no region was set yet, returns + * a 4-component integer vector with all components set to -1. To check this, + * you should call ShadowSource::has_region() first. + * + * @return [description] + */ +inline const LVecBase4i& ShadowSource::get_region() const { + return _region; +} + +/** + * @brief Returns the assigned region of the source in UV space. + * @details This returns the region of the source, in UV space. This is the + * region set by ShadowSource::set_region. If no region was set yet, returns + * a 4-component integer vector with all components set to -1. To check this, + * you should call ShadowSource::has_region() first. + * + * @return [description] + */ +inline const LVecBase4& ShadowSource::get_uv_region() const { + return _region_uv; +} + +/** + * @brief Sets the assigned region of the source in atlas and uv space. + * @details This sets the assigned region of the ShadowSource. The region in + * atlas space should be the region returned from the + * ShadowAtlas::find_and_reserve_region. The uv-region should be the same region, + * but in the 0 .. 1 range (can be converted with ShadowAtlas::region_to_uv). + * This is required for the shaders, because they expect coordinates in the + * 0 .. 1 range for sampling. + * + * @param region Atlas-Space region + * @param region_uv UV-Space region + */ +inline void ShadowSource::set_region(const LVecBase4i& region, const LVecBase4& region_uv) { + _region = region; + _region_uv = region_uv; +} + +/** + * @brief Returns the View-Projection matrix of the source. + * @details This returns the current view-projection matrix of the ShadowSource. + * If no matrix was set yet, returns a matrix with all components zero. + * If a matrix was set with ShadowSource::set_matrix_lens, returns the matrix + * set by that function call. + * + * If a matrix was set with ShadowSource::set_perspective_lens, returns a + * perspective view-projection matrix setup by those parameters. + * + * The matrix returned is the matrix used for rendering the shadow map, and + * includes the camera transform as well as the projection matrix. + * + * @return View-Projection matrix. + */ +inline const LMatrix4& ShadowSource::get_mvp() const { + return _mvp; +} + +/** + * @brief Writes the source to a GPUCommand. + * @details This writes the ShadowSource to a GPUCommand. This stores the + * mvp and the uv-region in the command. + * + * @param cmd GPUCommand to write to. + */ +inline void ShadowSource::write_to_command(GPUCommand &cmd) const { + // When storing on the gpu, we should already have a valid slot + nassertv(_slot >= 0); + cmd.push_mat4(_mvp); + cmd.push_vec4(_region_uv); +} + +/** + * @brief Sets the resolution of the source. + * @details This sets the resolution of the ShadowSource, in pixels. It should be + * a multiple of the tile size of the ShadowAtlas, and greater than zero. + * + * @param resolution [description] + */ +inline void ShadowSource::set_resolution(size_t resolution) { + nassertv(resolution > 0); + _resolution = resolution; + set_needs_update(true); +} + +/** + * @brief Returns the shadow sources bounds + * @details This returns the bounds of the shadow source, approximated as a sphere + * @return Bounds as a BoundingSphere + */ +inline const BoundingSphere& ShadowSource::get_bounds() const { + return _bounds; +} + +/** + * @brief Clears the assigned region of the source + * @details This unassigns any shadow atlas region from the source, previously + * set with set_region + */ +inline void ShadowSource::clear_region() { + _region.fill(-1); + _region_uv.fill(0); +} diff --git a/contrib/src/rplight/shadowSource.cxx b/contrib/src/rplight/shadowSource.cxx new file mode 100644 index 0000000000..e5b76b75b5 --- /dev/null +++ b/contrib/src/rplight/shadowSource.cxx @@ -0,0 +1,41 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "shadowSource.h" + +/** + * @brief Constructs a new shadow source + * @details This constructs a new shadow source, with no projection setup, + * and no slot assigned. + */ +ShadowSource::ShadowSource() { + _slot = -1; + _needs_update = true; + _resolution = 512; + _mvp.fill(0.0); + _region.fill(-1); + _region_uv.fill(0); +} diff --git a/contrib/src/rplight/shadowSource.h b/contrib/src/rplight/shadowSource.h new file mode 100644 index 0000000000..84b00a197b --- /dev/null +++ b/contrib/src/rplight/shadowSource.h @@ -0,0 +1,93 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#ifndef SHADOWSOURCE_H +#define SHADOWSOURCE_H + +#include "pandabase.h" +#include "luse.h" +#include "transformState.h" +#include "look_at.h" +#include "compose_matrix.h" +#include "perspectiveLens.h" +#include "boundingVolume.h" +#include "boundingSphere.h" +#include "boundingHexahedron.h" +#include "geometricBoundingVolume.h" + +#include "gpuCommand.h" + +/** + * @brief This class represents a single shadow source. + * @details The ShadowSource can be seen as a Camera. It is used by the Lights + * to render their shadows. Each ShadowSource has a position in the atlas, + * and a view-projection matrix. The shadow manager regenerates the shadow maps + * using the data from the shadow sources. + */ +class ShadowSource { +public: + ShadowSource(); + + inline void write_to_command(GPUCommand &cmd) const; + + inline void set_needs_update(bool flag); + inline void set_slot(int slot); + inline void set_region(const LVecBase4i& region, const LVecBase4& region_uv); + inline void set_resolution(size_t resolution); + inline void set_perspective_lens(PN_stdfloat fov, PN_stdfloat near_plane, + PN_stdfloat far_plane, LVecBase3 pos, + LVecBase3 direction); + inline void set_matrix_lens(const LMatrix4& mvp); + + inline bool has_region() const; + inline bool has_slot() const; + + inline void clear_region(); + + inline int get_slot() const; + inline bool get_needs_update() const; + inline size_t get_resolution() const; + inline const LMatrix4& get_mvp() const; + inline const LVecBase4i& get_region() const; + inline const LVecBase4& get_uv_region() const; + + inline const BoundingSphere& get_bounds() const; + +private: + int _slot; + bool _needs_update; + size_t _resolution; + LMatrix4 _mvp; + LVecBase4i _region; + LVecBase4 _region_uv; + + BoundingSphere _bounds; +}; + +#include "shadowSource.I" + +#endif // SHADOWSOURCE_H diff --git a/contrib/src/rplight/tagStateManager.I b/contrib/src/rplight/tagStateManager.I new file mode 100644 index 0000000000..058832e60d --- /dev/null +++ b/contrib/src/rplight/tagStateManager.I @@ -0,0 +1,93 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +/** + * @brief Registers a new camera which renders a certain pass + * @details This registers a new camera which will be used to render the given + * pass. The TagStateManager will keep track of the camera and + * applies all registered states onto the camera with Camera::set_tag_state. + * It also applies the appropriate camera mask to the camera, + * and sets an initial state to disable color write depending on the pass. + * + * @param source Camera which will be used to render shadows + */ +inline void TagStateManager:: +register_camera(const string& name, Camera* source) { + ContainerList::iterator entry = _containers.find(name); + nassertv(entry != _containers.end()); + register_camera(entry->second, source); +} + +/** + * @brief Unregisters a camera from the list of shadow cameras + * @details This unregisters a camera from the list of shadows cameras. It also + * resets all tag states of the camera, and also its initial state. + * + * @param source Camera to unregister + */ +inline void TagStateManager:: +unregister_camera(const string& name, Camera* source) { + ContainerList::iterator entry = _containers.find(name); + nassertv(entry != _containers.end()); + unregister_camera(entry->second, source); +} + +/** + * @brief Applies a given state for a pass to a NodePath + * @details This applies a shader to the given NodePath which is used when the + * NodePath is rendered by any registered camera for that pass. + * It also disables color write depending on the pass. + * + * @param np The nodepath to apply the shader to + * @param shader A handle to the shader to apply + * @param name Name of the state, should be a unique identifier + * @param sort Determines the sort with which the shader will be applied. + */ +inline void TagStateManager:: +apply_state(const string& state, NodePath np, Shader* shader, + const string &name, int sort) { + ContainerList::iterator entry = _containers.find(state); + nassertv(entry != _containers.end()); + apply_state(entry->second, np, shader, name, sort); +} + +/** + * @brief Returns the render mask for the given state + * @details This returns the mask of a given render pass, which can be used + * to either show or hide objects from this pass. + * + * @param container_name Name of the render-pass + * @return Bit mask of the render pass + */ +inline BitMask32 TagStateManager:: +get_mask(const string &container_name) { + if (container_name == "gbuffer") { + return BitMask32::bit(1); + } + ContainerList::iterator entry = _containers.find(container_name); + nassertr(entry != _containers.end(), BitMask32()); + return entry->second.mask; +} diff --git a/contrib/src/rplight/tagStateManager.cxx b/contrib/src/rplight/tagStateManager.cxx new file mode 100644 index 0000000000..ddc996f9a4 --- /dev/null +++ b/contrib/src/rplight/tagStateManager.cxx @@ -0,0 +1,202 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#include "tagStateManager.h" + + +NotifyCategoryDef(tagstatemgr, ""); + +/** + * @brief Constructs a new TagStateManager + * @details This constructs a new TagStateManager. The #main_cam_node should + * refer to the main scene camera, and will most likely be base.cam. + * It is necessary to pass the camera because the C++ code does not have + * access to the showbase. + * + * @param main_cam_node The main scene camera + */ +TagStateManager:: +TagStateManager(NodePath main_cam_node) { + nassertv(!main_cam_node.is_empty()); + nassertv(DCAST(Camera, main_cam_node.node()) != nullptr); + _main_cam_node = main_cam_node; + + // Set default camera mask + DCAST(Camera, _main_cam_node.node())->set_camera_mask(BitMask32::bit(1)); + + // Init containers + _containers["shadow"] = StateContainer("Shadows", 2, false); + _containers["voxelize"] = StateContainer("Voxelize", 3, false); + _containers["envmap"] = StateContainer("Envmap", 4, true); + _containers["forward"] = StateContainer("Forward", 5, true); +} + +/** + * @brief Destructs the TagStateManager + * @details This destructs the TagStateManager, and cleans up all resources used. + */ +TagStateManager:: +~TagStateManager() { + cleanup_states(); +} + +/** + * @brief Applies a given state to a NodePath + * @details This applies a shader to the given NodePath which is used when the + * NodePath is rendered by any registered camera of the container. + * + * @param container The container which is used to store the state + * @param np The nodepath to apply the shader to + * @param shader A handle to the shader to apply + * @param name Name of the state, should be a unique identifier + * @param sort Changes the sort with which the shader will be applied. + */ +void TagStateManager:: +apply_state(StateContainer& container, NodePath np, Shader* shader, + const string &name, int sort) { + if (tagstatemgr_cat.is_spam()) { + tagstatemgr_cat.spam() << "Constructing new state " << name + << " with shader " << shader << endl; + } + + // Construct the render state + CPT(RenderState) state = RenderState::make_empty(); + + // Disable color write for all stages except the environment container + if (!container.write_color) { + state = state->set_attrib(ColorWriteAttrib::make(ColorWriteAttrib::C_off), 10000); + } + state = state->set_attrib(ShaderAttrib::make(shader, sort), sort); + + // Emit a warning if we override an existing state + if (container.tag_states.count(name) != 0) { + tagstatemgr_cat.warning() << "Overriding existing state " << name << endl; + } + + // Store the state, this is required whenever we attach a new camera, so + // it can also track the existing states + container.tag_states[name] = state; + + // Save the tag on the node path + np.set_tag(container.tag_name, name); + + // Apply the state on all cameras which are attached so far + for (size_t i = 0; i < container.cameras.size(); ++i) { + container.cameras[i]->set_tag_state(name, state); + } +} + +/** + * @brief Cleans up all registered states. + * @details This cleans up all states which were registered to the TagStateManager. + * It also calls Camera::clear_tag_states() on the main_cam_node and all attached + * cameras. + */ +void TagStateManager:: +cleanup_states() { + if (tagstatemgr_cat.is_info()) { + tagstatemgr_cat.info() << "cleaning up states" << endl; + } + + // Clear all tag states of the main camera + DCAST(Camera, _main_cam_node.node())->clear_tag_states(); + + // Clear the containers + // XXX: Just iterate over the _container map + cleanup_container_states(_containers["shadow"]); + cleanup_container_states(_containers["voxelize"]); + cleanup_container_states(_containers["envmap"]); + cleanup_container_states(_containers["forward"]); +} + +/** + * @brief Cleans up the states of a given container + * @details This cleans all tag states of the given container, + * and also calls Camera::clear_tag_states on every assigned camera. + * + * @param container Container to clear + */ +void TagStateManager:: +cleanup_container_states(StateContainer& container) { + for (size_t i = 0; i < container.cameras.size(); ++i) { + container.cameras[i]->clear_tag_states(); + } + container.tag_states.clear(); +} + +/** + * @brief Registers a new camera to a given container + * @details This registers a new camera to a container, and sets its initial + * state as well as the camera mask. + * + * @param container The container to add the camera to + * @param source The camera to add + */ +void TagStateManager:: +register_camera(StateContainer& container, Camera* source) { + source->set_tag_state_key(container.tag_name); + source->set_camera_mask(container.mask); + + // Construct an initial state which also disables color write, additionally + // to the ColorWriteAttrib on each unique state. + CPT(RenderState) state = RenderState::make_empty(); + + if (!container.write_color) { + state = state->set_attrib(ColorWriteAttrib::make(ColorWriteAttrib::C_off), 10000); + } + source->set_initial_state(state); + + // Store the camera so we can keep track of it + container.cameras.push_back(source); +} + +/** + * @brief Unregisters a camera from a container + * @details This unregisters a camera from the list of cameras of a given + * container. It also resets all tag states of the camera, and also its initial + * state. + * + * @param source Camera to unregister + */ +void TagStateManager:: +unregister_camera(StateContainer& container, Camera* source) { + CameraList& cameras = container.cameras; + + // Make sure the camera was attached so far + if (std::find(cameras.begin(), cameras.end(), source) == cameras.end()) { + tagstatemgr_cat.error() + << "Called unregister_camera but camera was never registered!" << endl; + return; + } + + // Remove the camera from the list of attached cameras + cameras.erase(std::remove(cameras.begin(), cameras.end(), source), cameras.end()); + + // Reset the camera + source->clear_tag_states(); + source->set_initial_state(RenderState::make_empty()); +} diff --git a/contrib/src/rplight/tagStateManager.h b/contrib/src/rplight/tagStateManager.h new file mode 100644 index 0000000000..6c26e5cbf6 --- /dev/null +++ b/contrib/src/rplight/tagStateManager.h @@ -0,0 +1,93 @@ +/** + * + * RenderPipeline + * + * Copyright (c) 2014-2016 tobspr + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef TAGSTATEMANAGER_H +#define TAGSTATEMANAGER_H + +#include "pandabase.h" +#include "bitMask.h" +#include "camera.h" +#include "nodePath.h" +#include "shader.h" +#include "renderState.h" +#include "shaderAttrib.h" +#include "colorWriteAttrib.h" + +NotifyCategoryDecl(tagstatemgr, EXPORT_CLASS, EXPORT_TEMPL); + +/** + * @brief This class handles all different tag states + * @details The TagStateManager stores a list of RenderStates assigned to different + * steps in the pipeline. For example, there are a list of shadow states, which + * are applied whenever objects are rendered from a shadow camera. + * + * The Manager also stores a list of all cameras used in the different stages, + * to keep track of the states used and to be able to attach new states. + */ +class TagStateManager { +PUBLISHED: + TagStateManager(NodePath main_cam_node); + ~TagStateManager(); + + inline void apply_state(const string& state, NodePath np, Shader* shader, const string &name, int sort); + void cleanup_states(); + + inline void register_camera(const string& state, Camera* source); + inline void unregister_camera(const string& state, Camera* source); + inline BitMask32 get_mask(const string &container_name); + +private: + typedef vector CameraList; + typedef pmap TagStateList; + + struct StateContainer { + CameraList cameras; + TagStateList tag_states; + string tag_name; + BitMask32 mask; + bool write_color; + + StateContainer() {}; + StateContainer(const string &tag_name, size_t mask, bool write_color) + : tag_name(tag_name), mask(BitMask32::bit(mask)), write_color(write_color) {}; + }; + + void apply_state(StateContainer& container, NodePath np, Shader* shader, + const string& name, int sort); + void cleanup_container_states(StateContainer& container); + void register_camera(StateContainer &container, Camera* source); + void unregister_camera(StateContainer &container, Camera* source); + + typedef pmap ContainerList; + ContainerList _containers; + + NodePath _main_cam_node; +}; + + +#include "tagStateManager.I" + +#endif // TAGSTATEMANAGER_H diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index e06a90a83f..5e7d59443a 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -6497,6 +6497,29 @@ if (PkgSkip("CONTRIB")==0 and not RUNTIME): TargetAdd('ai.pyd', input=COMMON_PANDA_LIBS) TargetAdd('ai.pyd', opts=['PYTHON']) +# +# DIRECTORY: contrib/src/rplight/ +# +if not PkgSkip("CONTRIB") and not PkgSkip("PYTHON") and not RUNTIME: + OPTS=['DIR:contrib/src/rplight', 'BUILDING:RPLIGHT', 'PYTHON'] + TargetAdd('p3rplight_composite1.obj', opts=OPTS, input='p3rplight_composite1.cxx') + + IGATEFILES=GetDirectoryContents('contrib/src/rplight', ["*.h", "*_composite*.cxx"]) + TargetAdd('libp3rplight.in', opts=OPTS, input=IGATEFILES) + TargetAdd('libp3rplight.in', opts=['IMOD:panda3d._rplight', 'ILIB:libp3rplight', 'SRCDIR:contrib/src/rplight']) + TargetAdd('libp3rplight_igate.obj', input='libp3rplight.in', opts=["DEPENDENCYONLY"]) + + TargetAdd('rplight_module.obj', input='libp3rplight.in') + TargetAdd('rplight_module.obj', opts=OPTS) + TargetAdd('rplight_module.obj', opts=['IMOD:panda3d._rplight', 'ILIB:_rplight', 'IMPORT:panda3d.core']) + + TargetAdd('_rplight.pyd', input='rplight_module.obj') + TargetAdd('_rplight.pyd', input='libp3rplight_igate.obj') + TargetAdd('_rplight.pyd', input='p3rplight_composite1.obj') + TargetAdd('_rplight.pyd', input='libp3interrogatedb.dll') + TargetAdd('_rplight.pyd', input=COMMON_PANDA_LIBS) + TargetAdd('_rplight.pyd', opts=['PYTHON']) + # # Generate the models directory and samples directory # From ba9ea065e4701c823b05928e0c3269b284847388 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 8 Jan 2018 21:26:43 +0100 Subject: [PATCH 034/118] gobj: performance improvement for CPU animation In particular it seems that decomposition is slow, so the code to handle the transformation of the normal column now tries harder to avoid it, especially in the case of a scale of 1. Also see #222 --- panda/src/gobj/geomVertexData.cxx | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/panda/src/gobj/geomVertexData.cxx b/panda/src/gobj/geomVertexData.cxx index b692c36bf5..7d8e0e63f3 100644 --- a/panda/src/gobj/geomVertexData.cxx +++ b/panda/src/gobj/geomVertexData.cxx @@ -1836,17 +1836,21 @@ do_transform_vector_column(const GeomVertexFormat *format, GeomVertexRewriter &d bool normalize = false; if (data_column->get_contents() == C_normal) { // This is to preserve perpendicularity to the surface. - LVecBase3 scale, shear, hpr; - if (decompose_matrix(mat.get_upper_3(), scale, shear, hpr) && - IS_NEARLY_EQUAL(scale[0], scale[1]) && - IS_NEARLY_EQUAL(scale[0], scale[2])) { - if (scale[0] == 1) { + LVecBase3 scale_sq(mat.get_row3(0).length_squared(), + mat.get_row3(1).length_squared(), + mat.get_row3(2).length_squared()); + if (IS_THRESHOLD_EQUAL(scale_sq[0], scale_sq[1], 2.0e-3f) && + IS_THRESHOLD_EQUAL(scale_sq[0], scale_sq[2], 2.0e-3f)) { + // There is a uniform scale. + LVecBase3 scale, shear, hpr; + if (IS_THRESHOLD_EQUAL(scale_sq[0], 1, 2.0e-3f)) { // No scale to worry about. xform = mat; - } else { - // Simply take the uniform scale out of the transformation. Not sure - // if it might be better to just normalize? + } else if (decompose_matrix(mat.get_upper_3(), scale, shear, hpr)) { + // Make a new matrix with scale/translate taken out of the equation. compose_matrix(xform, LVecBase3(1, 1, 1), shear, hpr, LVecBase3::zero()); + } else { + normalize = true; } } else { // There is a non-uniform scale, so we need to do all this to preserve From dee4cd62017853f44bb54f6e6c764ba2b18cf90f Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 10 Jan 2018 20:14:08 +0100 Subject: [PATCH 035/118] bam2egg: support exposed joints properly Also, don't use structured tag unless it seems necessary to preserve the structure --- panda/src/egg2pg/eggSaver.cxx | 49 +++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/panda/src/egg2pg/eggSaver.cxx b/panda/src/egg2pg/eggSaver.cxx index 5eeb98f42c..63b114ed54 100644 --- a/panda/src/egg2pg/eggSaver.cxx +++ b/panda/src/egg2pg/eggSaver.cxx @@ -52,6 +52,7 @@ #include "modelNode.h" #include "animBundleNode.h" #include "animChannelMatrixXfmTable.h" +#include "characterJointEffect.h" #include "characterJoint.h" #include "character.h" #include "string_utils.h" @@ -155,6 +156,16 @@ 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); @@ -354,6 +365,17 @@ 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 != NULL) { @@ -384,16 +406,33 @@ 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; - recurse_nodes(node_path, egg_group, has_decal, &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); + } // turn it into a switch.. egg_group->set_switch_flag(true); From 6a323081c0e3e64df3b8496cb0ce28df2f21bcd1 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 10 Jan 2018 20:17:43 +0100 Subject: [PATCH 036/118] pipeline: fix regression with Thread.getCurrentTask() disappearing This method was accidentally un-exposed by 2e20a0f16ed4c08030f978e46950ba635fca8a32 Fixes #226 --- panda/src/event/asyncTask.cxx | 6 ++---- panda/src/pipeline/thread.I | 4 ++-- panda/src/pipeline/thread.h | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/panda/src/event/asyncTask.cxx b/panda/src/event/asyncTask.cxx index 17f0a0630d..9f70f0e5ad 100644 --- a/panda/src/event/asyncTask.cxx +++ b/panda/src/event/asyncTask.cxx @@ -402,8 +402,7 @@ unlock_and_do_task() { nassertr(current_thread->_current_task == nullptr, DS_interrupt); void *ptr = AtomicAdjust::compare_and_exchange_ptr - ((void * TVOLATILE &)current_thread->_current_task, - (void *)nullptr, (void *)this); + (current_thread->_current_task, nullptr, (TypedReferenceCount *)this); // If the return value is other than nullptr, someone else must have // assigned the task first, in another thread. That shouldn't be possible. @@ -437,8 +436,7 @@ unlock_and_do_task() { nassertr(current_thread->_current_task == this, status); ptr = AtomicAdjust::compare_and_exchange_ptr - ((void * TVOLATILE &)current_thread->_current_task, - (void *)this, (void *)nullptr); + (current_thread->_current_task, (TypedReferenceCount *)this, nullptr); // If the return value is other than this, someone else must have assigned // the task first, in another thread. That shouldn't be possible. diff --git a/panda/src/pipeline/thread.I b/panda/src/pipeline/thread.I index 12a8607e63..4fcd8a447e 100644 --- a/panda/src/pipeline/thread.I +++ b/panda/src/pipeline/thread.I @@ -277,9 +277,9 @@ preempt() { * AsyncTaskManager), if any, or NULL if the thread is not currently servicing * a task. */ -INLINE AsyncTask *Thread:: +INLINE TypedReferenceCount *Thread:: get_current_task() const { - return _current_task; + return (TypedReferenceCount *)_current_task; } /** diff --git a/panda/src/pipeline/thread.h b/panda/src/pipeline/thread.h index 4dbebacc8b..b519aa75a6 100644 --- a/panda/src/pipeline/thread.h +++ b/panda/src/pipeline/thread.h @@ -89,7 +89,7 @@ PUBLISHED: BLOCKING INLINE void join(); INLINE void preempt(); - INLINE AsyncTask *get_current_task() const; + INLINE TypedReferenceCount *get_current_task() const; INLINE void set_python_index(int index); @@ -142,7 +142,7 @@ private: int _pipeline_stage; PStatsCallback *_pstats_callback; bool _joinable; - AsyncTask *_current_task; + AtomicAdjust::Pointer _current_task; int _python_index; From 1cada85e6bd7135905199e82d6606da246aa902a Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 15 Jan 2018 12:20:27 +0100 Subject: [PATCH 037/118] ShaderGenerator: fixes for combine alpha blending --- panda/src/pgraphnodes/shaderGenerator.cxx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/panda/src/pgraphnodes/shaderGenerator.cxx b/panda/src/pgraphnodes/shaderGenerator.cxx index 6b83c3ef14..3546af732d 100644 --- a/panda/src/pgraphnodes/shaderGenerator.cxx +++ b/panda/src/pgraphnodes/shaderGenerator.cxx @@ -1444,7 +1444,7 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { text << "\t result.rgb = "; text << combine_mode_as_string(tex, combine_rgb, false, i); text << ";\n\t result.a = "; - text << combine_mode_as_string(tex, combine_alpha, false, i); + text << combine_mode_as_string(tex, combine_alpha, true, i); text << ";\n"; } if (tex._flags & ShaderKey::TF_rgb_scale_2) { @@ -1667,6 +1667,7 @@ combine_source_as_string(const ShaderKey::TextureInfo &info, short num, bool alp csource << "saturate(1.0f - "; } switch (c_src) { + case TextureStage::CS_undefined: case TextureStage::CS_texture: csource << "tex" << texindex; break; @@ -1685,8 +1686,6 @@ combine_source_as_string(const ShaderKey::TextureInfo &info, short num, bool alp case TextureStage::CS_last_saved_result: csource << "last_saved_result"; break; - case TextureStage::CS_undefined: - break; } if (c_op == TextureStage::CO_one_minus_src_color || c_op == TextureStage::CO_one_minus_src_alpha) { From fe3dab192fa2cd65885d5197d40ac0138ad3c9a8 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 15 Jan 2018 23:27:46 +0100 Subject: [PATCH 038/118] gobj: slightly more efficient string handling in Shader::make() --- panda/src/gobj/shader.I | 21 +++++++++------------ panda/src/gobj/shader.cxx | 30 +++++++++++++++--------------- panda/src/gobj/shader.h | 21 +++++++++------------ 3 files changed, 33 insertions(+), 39 deletions(-) diff --git a/panda/src/gobj/shader.I b/panda/src/gobj/shader.I index fe74b2bda6..fcefc1e235 100644 --- a/panda/src/gobj/shader.I +++ b/panda/src/gobj/shader.I @@ -694,9 +694,9 @@ read_datagram(DatagramIterator &scan) { * */ INLINE Shader::ShaderFile:: -ShaderFile(const string &shared) : +ShaderFile(string shared) : _separate(false), - _shared(shared) + _shared(move(shared)) { } @@ -704,17 +704,14 @@ ShaderFile(const string &shared) : * */ INLINE Shader::ShaderFile:: -ShaderFile(const string &vertex, - const string &fragment, - const string &geometry, - const string &tess_control, - const string &tess_evaluation) : +ShaderFile(string vertex, string fragment, string geometry, + string tess_control, string tess_evaluation) : _separate(true), - _vertex(vertex), - _fragment(fragment), - _geometry(geometry), - _tess_control(tess_control), - _tess_evaluation(tess_evaluation) + _vertex(move(vertex)), + _fragment(move(fragment)), + _geometry(move(geometry)), + _tess_control(move(tess_control)), + _tess_evaluation(move(tess_evaluation)) { } diff --git a/panda/src/gobj/shader.cxx b/panda/src/gobj/shader.cxx index a2c54a5d0b..add7acef2f 100644 --- a/panda/src/gobj/shader.cxx +++ b/panda/src/gobj/shader.cxx @@ -3179,7 +3179,7 @@ load_compute(ShaderLanguage lang, const Filename &fn) { * Loads the shader, using the string as shader body. */ PT(Shader) Shader:: -make(const string &body, ShaderLanguage lang) { +make(string body, ShaderLanguage lang) { if (lang == SL_GLSL) { shader_cat.error() << "GLSL shaders must have separate shader bodies!\n"; @@ -3197,7 +3197,7 @@ make(const string &body, ShaderLanguage lang) { } #endif - ShaderFile sbody(body); + ShaderFile sbody(move(body)); if (cache_generated_shaders) { ShaderTable::const_iterator i = _make_table.find(sbody); @@ -3208,7 +3208,7 @@ make(const string &body, ShaderLanguage lang) { PT(Shader) shader = new Shader(lang); shader->_filename = ShaderFile("created-shader"); - shader->_text = sbody; + shader->_text = move(sbody); #ifdef HAVE_CG if (lang == SL_Cg) { @@ -3223,7 +3223,7 @@ make(const string &body, ShaderLanguage lang) { #endif if (cache_generated_shaders) { - _make_table[sbody] = shader; + _make_table[shader->_text] = shader; } if (dump_generated_shaders) { @@ -3235,7 +3235,7 @@ make(const string &body, ShaderLanguage lang) { pofstream s; s.open(fn.c_str(), ios::out | ios::trunc); - s << body; + s << shader->get_text(); s.close(); } return shader; @@ -3245,9 +3245,8 @@ make(const string &body, ShaderLanguage lang) { * Loads the shader, using the strings as shader bodies. */ PT(Shader) Shader:: -make(ShaderLanguage lang, const string &vertex, const string &fragment, - const string &geometry, const string &tess_control, - const string &tess_evaluation) { +make(ShaderLanguage lang, string vertex, string fragment, string geometry, + string tess_control, string tess_evaluation) { #ifndef HAVE_CG if (lang == SL_Cg) { shader_cat.error() << "Support for Cg shaders is not enabled.\n"; @@ -3260,7 +3259,8 @@ make(ShaderLanguage lang, const string &vertex, const string &fragment, return NULL; } - ShaderFile sbody(vertex, fragment, geometry, tess_control, tess_evaluation); + ShaderFile sbody(move(vertex), move(fragment), move(geometry), + move(tess_control), move(tess_evaluation)); if (cache_generated_shaders) { ShaderTable::const_iterator i = _make_table.find(sbody); @@ -3271,7 +3271,7 @@ make(ShaderLanguage lang, const string &vertex, const string &fragment, PT(Shader) shader = new Shader(lang); shader->_filename = ShaderFile("created-shader"); - shader->_text = sbody; + shader->_text = move(sbody); #ifdef HAVE_CG if (lang == SL_Cg) { @@ -3284,7 +3284,7 @@ make(ShaderLanguage lang, const string &vertex, const string &fragment, #endif if (cache_generated_shaders) { - _make_table[sbody] = shader; + _make_table[shader->_text] = shader; } return shader; @@ -3294,7 +3294,7 @@ make(ShaderLanguage lang, const string &vertex, const string &fragment, * Loads the compute shader from the given string. */ PT(Shader) Shader:: -make_compute(ShaderLanguage lang, const string &body) { +make_compute(ShaderLanguage lang, string body) { if (lang != SL_GLSL) { shader_cat.error() << "Only GLSL compute shaders are currently supported.\n"; @@ -3303,7 +3303,7 @@ make_compute(ShaderLanguage lang, const string &body) { ShaderFile sbody; sbody._separate = true; - sbody._compute = body; + sbody._compute = move(body); if (cache_generated_shaders) { ShaderTable::const_iterator i = _make_table.find(sbody); @@ -3314,10 +3314,10 @@ make_compute(ShaderLanguage lang, const string &body) { PT(Shader) shader = new Shader(lang); shader->_filename = ShaderFile("created-shader"); - shader->_text = sbody; + shader->_text = move(sbody); if (cache_generated_shaders) { - _make_table[sbody] = shader; + _make_table[shader->_text] = shader; } return shader; diff --git a/panda/src/gobj/shader.h b/panda/src/gobj/shader.h index 3b23b2d698..996ee1b7aa 100644 --- a/panda/src/gobj/shader.h +++ b/panda/src/gobj/shader.h @@ -84,7 +84,7 @@ PUBLISHED: }; static PT(Shader) load(const Filename &file, ShaderLanguage lang = SL_none); - static PT(Shader) make(const string &body, ShaderLanguage lang = SL_none); + static PT(Shader) make(string body, ShaderLanguage lang = SL_none); static PT(Shader) load(ShaderLanguage lang, const Filename &vertex, const Filename &fragment, const Filename &geometry = "", @@ -92,11 +92,11 @@ PUBLISHED: const Filename &tess_evaluation = ""); static PT(Shader) load_compute(ShaderLanguage lang, const Filename &fn); static PT(Shader) make(ShaderLanguage lang, - const string &vertex, const string &fragment, - const string &geometry = "", - const string &tess_control = "", - const string &tess_evaluation = ""); - static PT(Shader) make_compute(ShaderLanguage lang, const string &body); + string vertex, string fragment, + string geometry = "", + string tess_control = "", + string tess_evaluation = ""); + static PT(Shader) make_compute(ShaderLanguage lang, string body); INLINE Filename get_filename(ShaderType type = ST_none) const; INLINE void set_filename(ShaderType type, const Filename &filename); @@ -464,12 +464,9 @@ public: class ShaderFile : public ReferenceCount { public: INLINE ShaderFile() {}; - INLINE ShaderFile(const string &shared); - INLINE ShaderFile(const string &vertex, - const string &fragment, - const string &geometry, - const string &tess_control, - const string &tess_evaluation); + INLINE ShaderFile(string shared); + INLINE ShaderFile(string vertex, string fragment, string geometry, + string tess_control, string tess_evaluation); INLINE void write_datagram(Datagram &dg) const; INLINE void read_datagram(DatagramIterator &source); From 8f5d55c53957822ed79085f6b8b2b51f12e05301 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 17 Jan 2018 10:45:36 +0100 Subject: [PATCH 039/118] Update README; mention upgrading pip on failure Prompted by #231 [skip ci] --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 474ccedc57..fde01c38f0 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ into an existing Python installation is using the following command: pip install --pre --extra-index-url https://archive.panda3d.org/ panda3d ``` +If this command fails, please make sure your version of pip is up-to-date. + If you prefer to install the full SDK with all tools, the latest development builds can be obtained from this page: @@ -51,6 +53,9 @@ depending on whether you are on a 32-bit or 64-bit system: https://www.panda3d.org/download/panda3d-1.9.4/panda3d-1.9.4-tools-win32.zip https://www.panda3d.org/download/panda3d-1.9.4/panda3d-1.9.4-tools-win64.zip +(It is also possible to build using MSVC 2015 and 2017, which requires a +different set of thirdparty libraries, but that is not described here.) + After acquiring these dependencies, you may simply build Panda3D from the command prompt using the following command: From bfd5fa56fa607aa782e1796b2f7ac3d15bb6a09e Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 17 Jan 2018 17:08:08 +0100 Subject: [PATCH 040/118] rplight: compatibility with old macOS glibc --- contrib/src/rplight/pointerSlotStorage.h | 11 +++++++++++ contrib/src/rplight/pssmCameraRig.cxx | 2 +- contrib/src/rplight/shadowManager.cxx | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/contrib/src/rplight/pointerSlotStorage.h b/contrib/src/rplight/pointerSlotStorage.h index 11b4ace2bd..2d908c5795 100644 --- a/contrib/src/rplight/pointerSlotStorage.h +++ b/contrib/src/rplight/pointerSlotStorage.h @@ -38,7 +38,14 @@ class PointerSlotStorage {}; #include "pandabase.h" + +// Apple has an outdated libstdc++, so pull the class from TR1. +#if defined(__GLIBCXX__) && __GLIBCXX__ <= 20070719 +#include +using std::tr1::array; +#else #include +#endif /** * @brief Class to keep a list of pointers and nullpointers. @@ -58,7 +65,11 @@ public: * initialized to a nullptr. */ PointerSlotStorage() { +#if defined(__GLIBCXX__) && __GLIBCXX__ <= 20070719 + _data.assign(nullptr); +#else _data.fill(nullptr); +#endif _max_index = 0; _num_entries = 0; } diff --git a/contrib/src/rplight/pssmCameraRig.cxx b/contrib/src/rplight/pssmCameraRig.cxx index 98da3840f3..b9560a80d2 100644 --- a/contrib/src/rplight/pssmCameraRig.cxx +++ b/contrib/src/rplight/pssmCameraRig.cxx @@ -84,7 +84,7 @@ void PSSMCameraRig::init_cam_nodes() { lens->set_near_far(1, 1000); // Construct a new camera - _cameras[i] = new Camera("pssm-cam-" + to_string(((long long)i)), lens); + _cameras[i] = new Camera("pssm-cam-" + format_string(i), lens); _cam_nodes.push_back(NodePath(_cameras[i])); _max_film_sizes[i].fill(0); } diff --git a/contrib/src/rplight/shadowManager.cxx b/contrib/src/rplight/shadowManager.cxx index 8b7b1b1f96..be216c0075 100644 --- a/contrib/src/rplight/shadowManager.cxx +++ b/contrib/src/rplight/shadowManager.cxx @@ -79,7 +79,7 @@ void ShadowManager::init() { for(size_t i = 0; i < _max_updates; ++i) { // Create the camera - PT(Camera) camera = new Camera("ShadowCam-" + to_string((long long)i)); + PT(Camera) camera = new Camera("ShadowCam-" + format_string(i)); camera->set_lens(new MatrixLens()); camera->set_active(false); camera->set_scene(_scene_parent); From 4c4288f49174e915c70add242df1a1ff0bf00e08 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 17 Jan 2018 19:46:11 +0100 Subject: [PATCH 041/118] display: lock GraphicsEngine mutex in remove_all_windows() --- panda/src/display/graphicsEngine.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index 874ab9be13..f65c5377fc 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -583,6 +583,8 @@ void GraphicsEngine:: remove_all_windows() { Thread *current_thread = Thread::get_current_thread(); + ReMutexHolder holder(_lock, current_thread); + // Let's move the _windows vector into a local copy first, and walk through // that local copy, just in case someone we call during the loop attempts to // modify _windows. I don't know what code would be doing this, but it From fe0c1828301f376317066ae01e28aba32bb8463f Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 17 Jan 2018 19:46:30 +0100 Subject: [PATCH 042/118] cocoa: fix app launching regression on macOS The regression was introduced by ef7f856c46299f4d14711130269f11afad49e4c3 Fixes #227 --- panda/src/cocoadisplay/cocoaGraphicsPipe.mm | 1 - panda/src/cocoadisplay/cocoaGraphicsWindow.mm | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/panda/src/cocoadisplay/cocoaGraphicsPipe.mm b/panda/src/cocoadisplay/cocoaGraphicsPipe.mm index 0baa94f8ed..5430373c55 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsPipe.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsPipe.mm @@ -15,7 +15,6 @@ #include "cocoaGraphicsBuffer.h" #include "cocoaGraphicsWindow.h" #include "cocoaGraphicsStateGuardian.h" -#include "cocoaPandaApp.h" #include "config_cocoadisplay.h" #include "frameBufferProperties.h" #include "displayInformation.h" diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index 426c06cc78..39ecf09162 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm @@ -15,6 +15,7 @@ #include "cocoaGraphicsStateGuardian.h" #include "config_cocoadisplay.h" #include "cocoaGraphicsPipe.h" +#include "cocoaPandaApp.h" #include "graphicsPipe.h" #include "keyboardButton.h" @@ -68,6 +69,8 @@ CocoaGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, // Now that we know for sure we want a window, we can create the Cocoa app. // This will cause the application icon to appear and start bouncing. if (NSApp == nil) { + [CocoaPandaApp sharedApplication]; + #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; #endif From 3977b9c57c349a591a6db46fb0d8ef51a86e744d Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 18 Jan 2018 21:22:37 +0100 Subject: [PATCH 043/118] collide: fix multithreaded pipeline crash with CollisionVisualizer --- panda/src/collide/collisionVisualizer.I | 4 +++ panda/src/collide/collisionVisualizer.cxx | 34 +++++++++++++++++++---- panda/src/collide/collisionVisualizer.h | 3 ++ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/panda/src/collide/collisionVisualizer.I b/panda/src/collide/collisionVisualizer.I index 3c14f309cf..16e218f267 100644 --- a/panda/src/collide/collisionVisualizer.I +++ b/panda/src/collide/collisionVisualizer.I @@ -31,6 +31,7 @@ SolidInfo() { */ INLINE void CollisionVisualizer:: set_point_scale(PN_stdfloat point_scale) { + LightMutexHolder holder(_lock); _point_scale = point_scale; } @@ -39,6 +40,7 @@ set_point_scale(PN_stdfloat point_scale) { */ INLINE PN_stdfloat CollisionVisualizer:: get_point_scale() const { + LightMutexHolder holder(_lock); return _point_scale; } @@ -51,6 +53,7 @@ get_point_scale() const { */ INLINE void CollisionVisualizer:: set_normal_scale(PN_stdfloat normal_scale) { + LightMutexHolder holder(_lock); _normal_scale = normal_scale; } @@ -59,6 +62,7 @@ set_normal_scale(PN_stdfloat normal_scale) { */ INLINE PN_stdfloat CollisionVisualizer:: get_normal_scale() const { + LightMutexHolder holder(_lock); return _normal_scale; } diff --git a/panda/src/collide/collisionVisualizer.cxx b/panda/src/collide/collisionVisualizer.cxx index d58137ce68..204a39b6bd 100644 --- a/panda/src/collide/collisionVisualizer.cxx +++ b/panda/src/collide/collisionVisualizer.cxx @@ -41,7 +41,7 @@ TypeHandle CollisionVisualizer::_type_handle; * */ CollisionVisualizer:: -CollisionVisualizer(const string &name) : PandaNode(name) { +CollisionVisualizer(const string &name) : PandaNode(name), _lock("CollisionVisualizer") { set_cull_callback(); // We always want to render the CollisionVisualizer node itself (even if it @@ -51,6 +51,23 @@ CollisionVisualizer(const string &name) : PandaNode(name) { _normal_scale = 1.0f; } +/** + * Copy constructor. + */ +CollisionVisualizer:: +CollisionVisualizer(const CollisionVisualizer ©) : + PandaNode(copy), + _lock("CollisionVisualizer"), + _point_scale(copy._point_scale), + _normal_scale(copy._normal_scale) { + + set_cull_callback(); + + // We always want to render the CollisionVisualizer node itself (even if it + // doesn't appear to have any geometry within it). + set_internal_bounds(new OmniBoundingVolume()); +} + /** * */ @@ -64,6 +81,7 @@ CollisionVisualizer:: */ void CollisionVisualizer:: clear() { + LightMutexHolder holder(_lock); _data.clear(); } @@ -99,6 +117,8 @@ bool CollisionVisualizer:: cull_callback(CullTraverser *trav, CullTraverserData &data) { // Now we go through and actually draw our visualized collision solids. + LightMutexHolder holder(_lock); + Data::const_iterator di; for (di = _data.begin(); di != _data.end(); ++di) { const TransformState *net_transform = (*di).first; @@ -257,6 +277,7 @@ output(ostream &out) const { void CollisionVisualizer:: begin_traversal() { CollisionRecorder::begin_traversal(); + LightMutexHolder holder(_lock); _data.clear(); } @@ -271,12 +292,13 @@ collision_tested(const CollisionEntry &entry, bool detected) { NodePath node_path = entry.get_into_node_path(); CPT(TransformState) net_transform = node_path.get_net_transform(); - const CollisionSolid *solid = entry.get_into(); - nassertv(solid != (CollisionSolid *)NULL); + CPT(CollisionSolid) solid = entry.get_into(); + nassertv(!solid.is_null()); - VizInfo &viz_info = _data[net_transform]; + LightMutexHolder holder(_lock); + VizInfo &viz_info = _data[move(net_transform)]; if (detected) { - viz_info._solids[solid]._detected_count++; + viz_info._solids[move(solid)]._detected_count++; if (entry.has_surface_point()) { CollisionPoint p; @@ -286,7 +308,7 @@ collision_tested(const CollisionEntry &entry, bool detected) { } } else { - viz_info._solids[solid]._missed_count++; + viz_info._solids[move(solid)]._missed_count++; } } diff --git a/panda/src/collide/collisionVisualizer.h b/panda/src/collide/collisionVisualizer.h index 29d920f92d..88491c4e26 100644 --- a/panda/src/collide/collisionVisualizer.h +++ b/panda/src/collide/collisionVisualizer.h @@ -20,6 +20,7 @@ #include "collisionSolid.h" #include "nodePath.h" #include "pmap.h" +#include "lightMutex.h" #ifdef DO_COLLISION_RECORDING @@ -34,6 +35,7 @@ class EXPCL_PANDA_COLLIDE CollisionVisualizer : public PandaNode, public CollisionRecorder { PUBLISHED: explicit CollisionVisualizer(const string &name); + CollisionVisualizer(const CollisionVisualizer ©); virtual ~CollisionVisualizer(); INLINE void set_point_scale(PN_stdfloat point_scale); @@ -89,6 +91,7 @@ private: Points _points; }; + LightMutex _lock; typedef pmap Data; Data _data; From 234758707769457ce6edfc7dfeaedd4a7b0d8961 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 24 Jan 2018 19:11:48 +0100 Subject: [PATCH 044/118] x11: respect cursor updates when in confined/relative mouse mode --- panda/src/x11display/x11GraphicsWindow.cxx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/panda/src/x11display/x11GraphicsWindow.cxx b/panda/src/x11display/x11GraphicsWindow.cxx index 8efbf01b5d..c57a580aa7 100644 --- a/panda/src/x11display/x11GraphicsWindow.cxx +++ b/panda/src/x11display/x11GraphicsWindow.cxx @@ -683,6 +683,12 @@ set_properties_now(WindowProperties &properties) { } else { XDefineCursor(_display, _xwindow, None); } + + // Regrab the mouse if we changed the cursor, otherwise it won't update. + if (!properties.has_mouse_mode() && + _properties.get_mouse_mode() != WindowProperties::M_absolute) { + properties.set_mouse_mode(_properties.get_mouse_mode()); + } } if (properties.has_foreground()) { From f2cf391a2e87870a5abf7f8369f8e01e44c73dfe Mon Sep 17 00:00:00 2001 From: fireclawthefox Date: Wed, 24 Jan 2018 19:00:34 +0100 Subject: [PATCH 045/118] gui: add simple way to enable overflow in DirectEntry Closes #236 --- direct/src/gui/DirectEntry.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/direct/src/gui/DirectEntry.py b/direct/src/gui/DirectEntry.py index 2eb7607400..6955e550a2 100644 --- a/direct/src/gui/DirectEntry.py +++ b/direct/src/gui/DirectEntry.py @@ -59,6 +59,8 @@ class DirectEntry(DirectFrame): # Text used for the PGEntry text node # NOTE: This overrides the DirectFrame text option ('initialText', '', DGG.INITOPT), + # Enable or disable text overflow scrolling + ('overflow', 0, self.setOverflowMode), # Command to be called on hitting Enter ('command', None, None), ('extraArgs', [], None), @@ -159,6 +161,9 @@ class DirectEntry(DirectFrame): def setCursorKeysActive(self): PGEntry.setCursorKeysActive(self.guiItem, self['cursorKeys']) + def setOverflowMode(self): + PGEntry.set_overflow_mode(self.guiItem, self['overflow']) + def setObscureMode(self): PGEntry.setObscureMode(self.guiItem, self['obscured']) From d6cf5752258f0eb202f9e5b4acd253fefae83e40 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 26 Jan 2018 14:30:21 +0100 Subject: [PATCH 046/118] pgraph: fix LightAttrib.compose resulting in wrong on_light values --- panda/src/pgraph/lightAttrib.cxx | 3 ++ tests/pgraph/test_lightattrib.py | 74 ++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 tests/pgraph/test_lightattrib.py diff --git a/panda/src/pgraph/lightAttrib.cxx b/panda/src/pgraph/lightAttrib.cxx index 15b010336e..40014c53ee 100644 --- a/panda/src/pgraph/lightAttrib.cxx +++ b/panda/src/pgraph/lightAttrib.cxx @@ -841,6 +841,9 @@ compose_impl(const RenderAttrib *other) const { lobj->attrib_ref(); } + // This is needed since _sorted_on_lights is not yet populated. + new_attrib->_sort_seq = UpdateSeq::old(); + return return_new(new_attrib); } diff --git a/tests/pgraph/test_lightattrib.py b/tests/pgraph/test_lightattrib.py new file mode 100644 index 0000000000..752f4c2717 --- /dev/null +++ b/tests/pgraph/test_lightattrib.py @@ -0,0 +1,74 @@ +from panda3d import core + +# Some dummy lights we can use for our light attributes. +spot = core.NodePath(core.Spotlight("spot")) +point = core.NodePath(core.PointLight("point")) +ambient = core.NodePath(core.AmbientLight("ambient")) + + +def test_lightattrib_compose_add(): + # Tests a case in which a child node adds another light. + lattr1 = core.LightAttrib.make() + lattr1 = lattr1.add_on_light(spot) + + lattr2 = core.LightAttrib.make() + lattr2 = lattr2.add_on_light(point) + + lattr3 = lattr1.compose(lattr2) + assert lattr3.get_num_on_lights() == 2 + + assert spot in lattr3.on_lights + assert point in lattr3.on_lights + + +def test_lightattrib_compose_subtract(): + # Tests a case in which a child node disables a light. + lattr1 = core.LightAttrib.make() + lattr1 = lattr1.add_on_light(spot) + lattr1 = lattr1.add_on_light(point) + + lattr2 = core.LightAttrib.make() + lattr2 = lattr2.add_off_light(ambient) + lattr2 = lattr2.add_off_light(point) + + lattr3 = lattr1.compose(lattr2) + assert lattr3.get_num_on_lights() == 1 + + assert spot in lattr3.on_lights + assert point not in lattr3.on_lights + assert ambient not in lattr3.on_lights + + +def test_lightattrib_compose_both(): + # Tests a case in which a child node both enables and disables a light. + lattr1 = core.LightAttrib.make() + lattr1 = lattr1.add_on_light(spot) + lattr1 = lattr1.add_on_light(point) + + lattr2 = core.LightAttrib.make() + lattr2 = lattr2.add_on_light(ambient) + lattr2 = lattr2.add_on_light(spot) + lattr2 = lattr2.add_off_light(point) + + lattr3 = lattr1.compose(lattr2) + assert lattr3.get_num_on_lights() == 2 + + assert spot in lattr3.on_lights + assert point not in lattr3.on_lights + assert ambient in lattr3.on_lights + + +def test_lightattrib_compose_alloff(): + # Tests a case in which a child node disables all lights. + lattr1 = core.LightAttrib.make() + lattr1 = lattr1.add_on_light(spot) + lattr1 = lattr1.add_on_light(point) + assert lattr1.get_num_on_lights() == 2 + + lattr2 = core.LightAttrib.make_all_off() + assert lattr2.has_all_off() + + lattr3 = lattr1.compose(lattr2) + assert lattr3.get_num_on_lights() == 0 + assert lattr3.get_num_off_lights() == 0 + assert lattr3.has_all_off() From f3aaa12adc840ca2e284cc5d80b4ed0d80eb686b Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 26 Jan 2018 14:31:40 +0100 Subject: [PATCH 047/118] tinydisplay: support texture clear, gracefully handle 0x0 textures --- .../tinydisplay/tinyGraphicsStateGuardian.cxx | 108 ++++++++++-------- 1 file changed, 63 insertions(+), 45 deletions(-) diff --git a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx index 9da9aa0fea..a9c6b113a2 100644 --- a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx +++ b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx @@ -2397,9 +2397,6 @@ upload_texture(TinyTextureContext *gtc, bool force, bool uses_mipmaps) { PStatTimer timer(_load_texture_pcollector); CPTA_uchar src_image = tex->get_uncompressed_ram_image(); - if (src_image.is_null()) { - return false; - } #ifdef DO_PSTATS _data_transferred_pcollector.add_level(tex->get_ram_image_size()); @@ -2438,56 +2435,70 @@ upload_texture(TinyTextureContext *gtc, bool force, bool uses_mipmaps) { for (int level = 0; level < gltex->num_levels; ++level) { ZTextureLevel *dest = &gltex->levels[level]; - switch (tex->get_format()) { - case Texture::F_rgb: - case Texture::F_rgb5: - case Texture::F_rgb8: - case Texture::F_rgb12: - case Texture::F_rgb332: - copy_rgb_image(dest, xsize, ysize, gtc, level); - break; + if (tex->has_ram_mipmap_image(level)) { + switch (tex->get_format()) { + case Texture::F_rgb: + case Texture::F_rgb5: + case Texture::F_rgb8: + case Texture::F_rgb12: + case Texture::F_rgb332: + copy_rgb_image(dest, xsize, ysize, gtc, level); + break; - case Texture::F_rgba: - case Texture::F_rgbm: - case Texture::F_rgba4: - case Texture::F_rgba5: - case Texture::F_rgba8: - case Texture::F_rgba12: - case Texture::F_rgba16: - case Texture::F_rgba32: - copy_rgba_image(dest, xsize, ysize, gtc, level); - break; + case Texture::F_rgba: + case Texture::F_rgbm: + case Texture::F_rgba4: + case Texture::F_rgba5: + case Texture::F_rgba8: + case Texture::F_rgba12: + case Texture::F_rgba16: + case Texture::F_rgba32: + copy_rgba_image(dest, xsize, ysize, gtc, level); + break; - case Texture::F_luminance: - copy_lum_image(dest, xsize, ysize, gtc, level); - break; + case Texture::F_luminance: + copy_lum_image(dest, xsize, ysize, gtc, level); + break; - case Texture::F_red: - copy_one_channel_image(dest, xsize, ysize, gtc, level, 0); - break; + case Texture::F_red: + copy_one_channel_image(dest, xsize, ysize, gtc, level, 0); + break; - case Texture::F_green: - copy_one_channel_image(dest, xsize, ysize, gtc, level, 1); - break; + case Texture::F_green: + copy_one_channel_image(dest, xsize, ysize, gtc, level, 1); + break; - case Texture::F_blue: - copy_one_channel_image(dest, xsize, ysize, gtc, level, 2); - break; + case Texture::F_blue: + copy_one_channel_image(dest, xsize, ysize, gtc, level, 2); + break; - case Texture::F_alpha: - copy_alpha_image(dest, xsize, ysize, gtc, level); - break; + case Texture::F_alpha: + copy_alpha_image(dest, xsize, ysize, gtc, level); + break; - case Texture::F_luminance_alphamask: - case Texture::F_luminance_alpha: - copy_la_image(dest, xsize, ysize, gtc, level); - break; + case Texture::F_luminance_alphamask: + case Texture::F_luminance_alpha: + copy_la_image(dest, xsize, ysize, gtc, level); + break; - default: - tinydisplay_cat.error() - << "Unsupported texture format " - << tex->get_format() << "!\n"; - return false; + default: + tinydisplay_cat.error() + << "Unsupported texture format " + << tex->get_format() << "!\n"; + return false; + } + } else { + // Fill the mipmap with a solid color. + LColor scaled = tex->get_clear_color().fmin(LColor(1)).fmax(LColor::zero()); + scaled *= 255; + unsigned int clear = RGBA8_TO_PIXEL((int)scaled[0], (int)scaled[1], + (int)scaled[2], (int)scaled[3]); + unsigned int *dpix = (unsigned int *)dest->pixmap; + int pixel_count = xsize * ysize; + while (pixel_count-- > 0) { + *dpix = clear; + ++dpix; + } } bytecount += xsize * ysize * 4; @@ -2559,6 +2570,13 @@ upload_simple_texture(TinyTextureContext *gtc) { */ bool TinyGraphicsStateGuardian:: setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels) { + if (x_size == 0 || y_size == 0) { + // A texture without pixels gets turned into a 1x1 texture. + x_size = 1; + y_size = 1; + num_levels = 1; + } + int s_bits = get_tex_shift(x_size); int t_bits = get_tex_shift(y_size); From 6a1d717e90312bf7a24bb9e42924992a5550176e Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 26 Jan 2018 14:33:13 +0100 Subject: [PATCH 048/118] dxgsg9: support texture clear, gracefully handle 0x0 textures --- panda/src/dxgsg9/dxTextureContext9.cxx | 37 +++++++++++++++++++++----- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/panda/src/dxgsg9/dxTextureContext9.cxx b/panda/src/dxgsg9/dxTextureContext9.cxx index babfae755e..c089c26e8a 100644 --- a/panda/src/dxgsg9/dxTextureContext9.cxx +++ b/panda/src/dxgsg9/dxTextureContext9.cxx @@ -365,6 +365,12 @@ create_texture(DXScreenData &scrn) { shrink_original = true; } + if (target_width == 0 || target_height == 0) { + // We can't create a zero-sized texture, so change it to 1x1. + target_width = 1; + target_height = 1; + } + const char *error_message; error_message = "create_texture failed: couldn't find compatible device Texture Pixel Format for input texture"; @@ -1721,16 +1727,32 @@ HRESULT DXTextureContext9::fill_d3d_texture_mipmap_pixels(int mip_level, int dep bool using_temp_buffer = false; HRESULT hr = E_FAIL; CPTA_uchar image = get_texture()->get_ram_mipmap_image(mip_level); - nassertr(!image.is_null(), E_FAIL); BYTE *pixels = (BYTE*) image.p(); DWORD width = (DWORD) get_texture()->get_expected_mipmap_x_size(mip_level); DWORD height = (DWORD) get_texture()->get_expected_mipmap_y_size(mip_level); int component_width = get_texture()->get_component_width(); - size_t view_size = get_texture()->get_ram_mipmap_view_size(mip_level); - pixels += view_size * get_view(); size_t page_size = get_texture()->get_expected_ram_mipmap_page_size(mip_level); - pixels += page_size * depth_index; + size_t view_size; + vector_uchar clear_data; + if (page_size > 0) { + if (image.is_null()) { + // Make an image, filled with the texture's clear color. + image = get_texture()->make_ram_mipmap_image(mip_level); + nassertr(!image.is_null(), E_FAIL); + pixels = (BYTE *)image.p(); + } + view_size = image.size(); + pixels += view_size * get_view(); + pixels += page_size * depth_index; + } else { + // This is a 0x0 texture, which gets loaded as though it were 1x1. + width = 1; + height = 1; + clear_data = get_texture()->get_clear_data(); + pixels = clear_data.data(); + view_size = clear_data.size(); + } if (get_texture()->get_texture_type() == Texture::TT_cube_map) { nassertr(IS_VALID_PTR(_d3d_cube_texture), E_FAIL); @@ -1909,7 +1931,9 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) { DWORD flags; D3DCOLOR color; - color = 0xFF000000; + LColor scaled = tex->get_clear_color().fmin(LColor(1)).fmax(LColor::zero()); + scaled *= 255; + color = D3DCOLOR_RGBA((int)scaled[0], (int)scaled[1], (int)scaled[2], (int)scaled[3]); flags = D3DCLEAR_TARGET; if (device -> Clear (NULL, NULL, flags, color, 0.0f, 0) == D3D_OK) { } @@ -1933,9 +1957,8 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) { return S_OK; } - return E_FAIL; } - nassertr(IS_VALID_PTR((BYTE*)image.p()), E_FAIL); + //nassertr(IS_VALID_PTR((BYTE*)image.p()), E_FAIL); nassertr(IS_VALID_PTR(_d3d_texture), E_FAIL); PStatTimer timer(GraphicsStateGuardian::_load_texture_pcollector); From 04352e152dc1634ffc9befe0ebc3e19a53ae6012 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 26 Jan 2018 15:39:30 +0100 Subject: [PATCH 049/118] ShaderGenerator: fix TextureStage mixup when stages are disabled This fixes a regression that caused the wrong textures/stage settings to be applied when a texture stage was being optimized out (eg. when a normal map is present while lighting is disabled). --- panda/src/pgraphnodes/shaderGenerator.cxx | 32 ++++++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/panda/src/pgraphnodes/shaderGenerator.cxx b/panda/src/pgraphnodes/shaderGenerator.cxx index 3546af732d..f741b02db8 100644 --- a/panda/src/pgraphnodes/shaderGenerator.cxx +++ b/panda/src/pgraphnodes/shaderGenerator.cxx @@ -409,11 +409,12 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) { } // In fact, perhaps this stage should be disabled altogether? + bool skip = false; switch (info._mode) { case TextureStage::M_normal: if (!shader_attrib->auto_normal_on() || (!key._lighting && (key._outputs & AuxBitplaneAttrib::ABO_aux_normal) == 0)) { - continue; + skip = true; } else { info._flags = ShaderKey::TF_map_normal; } @@ -422,24 +423,34 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) { if (shader_attrib->auto_glow_on()) { info._flags = ShaderKey::TF_map_glow; } else { - continue; + skip = true; } break; case TextureStage::M_gloss: if (key._lighting && shader_attrib->auto_gloss_on()) { info._flags = ShaderKey::TF_map_gloss; } else { - continue; + skip = true; } break; case TextureStage::M_height: if (parallax_mapping_samples > 0) { info._flags = ShaderKey::TF_map_height; } else { - continue; + skip = true; } break; } + // We can't just drop a disabled slot from the list, since then the + // indices for the texture stages will no longer match up. So we keep it, + // but set it to a noop state to indicate that it should be skipped. + if (skip) { + info._type = Texture::TT_1d_texture; + info._mode = TextureStage::M_modulate; + info._flags = 0; + key._textures.push_back(info); + continue; + } // Check if this state has a texture matrix to transform the texture // coordinates. @@ -947,6 +958,11 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { } for (size_t i = 0; i < key._textures.size(); ++i) { const ShaderKey::TextureInfo &tex = key._textures[i]; + if (tex._mode == TextureStage::M_modulate && tex._flags == 0) { + // Skip this stage. + continue; + } + text << "\t uniform sampler" << texture_type_as_string(tex._type) << " tex_" << i << ",\n"; if (tex._flags & ShaderKey::TF_has_texscale) { @@ -1023,6 +1039,10 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { // has a TexMatrixAttrib, also transform them. for (size_t i = 0; i < key._textures.size(); ++i) { const ShaderKey::TextureInfo &tex = key._textures[i]; + if (tex._mode == TextureStage::M_modulate && tex._flags == 0) { + // Skip this stage. + continue; + } switch (tex._gen_mode) { case TexGenAttrib::M_off: // Cg seems to be able to optimize this temporary away when appropriate. @@ -1099,6 +1119,10 @@ synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { } for (size_t i = 0; i < key._textures.size(); ++i) { ShaderKey::TextureInfo &tex = key._textures[i]; + if (tex._mode == TextureStage::M_modulate && tex._flags == 0) { + // Skip this stage. + continue; + } if ((tex._flags & ShaderKey::TF_map_height) == 0) { // Parallax mapping pushes the texture coordinates of the other textures // away from the camera. From 60a572f88a74c5bd7426a07b05c40ba52db184fa Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 28 Jan 2018 14:14:53 +0100 Subject: [PATCH 050/118] android: some changes for building on Android: - allow setting API target with --target=android-21 - always link to libpython on Android, seems to be necessary - support aarch64 (arm64-v8 ABI) architecture - enable building on an Android machine (tested in termux) [skip ci] --- dtool/src/dtoolbase/dtool_platform.h | 2 + makepanda/makepanda.py | 26 +++++++--- makepanda/makepandacore.py | 77 ++++++++++++++++++++++------ 3 files changed, 80 insertions(+), 25 deletions(-) diff --git a/dtool/src/dtoolbase/dtool_platform.h b/dtool/src/dtoolbase/dtool_platform.h index 4cb25abc3a..9037632738 100644 --- a/dtool/src/dtoolbase/dtool_platform.h +++ b/dtool/src/dtoolbase/dtool_platform.h @@ -51,6 +51,8 @@ #elif defined(__ANDROID__) #if defined(__ARM_ARCH_7A__) #define DTOOL_PLATFORM "android_armv7a" +#elif defined(__aarch64__) +#define DTOOL_PLATFORM "android_aarch64" #elif defined(__arm__) #define DTOOL_PLATFORM "android_arm" #elif defined(__mips__) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 5e7d59443a..ef02a1eb04 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -864,7 +864,7 @@ if (COMPILER=="GCC"): if not PkgSkip("PYTHON"): python_lib = SDK["PYTHONVERSION"] - if not RTDIST: + if not RTDIST and GetTarget() != 'android': # We don't link anything in the SDK with libpython. python_lib = "" SmartPkgEnable("PYTHON", "", python_lib, (SDK["PYTHONVERSION"], SDK["PYTHONVERSION"] + "/Python.h")) @@ -1259,8 +1259,11 @@ def CompileCxx(obj,src,opts): if GetTarget() == "android": # Most of the specific optimization flags here were # just copied from the default Android Makefiles. - cmd += ' -I%s/include' % (SDK["ANDROID_STL"]) - cmd += ' -I%s/libs/%s/include' % (SDK["ANDROID_STL"], SDK["ANDROID_ABI"]) + if "ANDROID_API" in SDK: + cmd += ' -D__ANDROID_API__=' + str(SDK["ANDROID_API"]) + if "ANDROID_STL" in SDK: + cmd += ' -I%s/include' % (SDK["ANDROID_STL"]) + cmd += ' -I%s/libs/%s/include' % (SDK["ANDROID_STL"], SDK["ANDROID_ABI"]) cmd += ' -ffunction-sections -funwind-tables' if arch == 'armv7a': cmd += ' -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__' @@ -1310,7 +1313,7 @@ def CompileCxx(obj,src,opts): if optlevel >= 4 or GetTarget() == "android": cmd += " -fno-rtti" - if ('SSE2' in opts or not PkgSkip("SSE2")) and not arch.startswith("arm"): + if ('SSE2' in opts or not PkgSkip("SSE2")) and not arch.startswith("arm") and arch != 'aarch64': cmd += " -msse2" # Needed by both Python, Panda, Eigen, all of which break aliasing rules. @@ -1437,12 +1440,19 @@ def CompileIgate(woutd,wsrc,opts): cmd += ' -D_MSC_VER=1600 -D"__declspec(param)=" -D__cdecl -D_near -D_far -D__near -D__far -D__stdcall' if (COMPILER=="GCC"): cmd += ' -D__attribute__\(x\)=' - if GetTargetArch() in ("x86_64", "amd64"): + target_arch = GetTargetArch() + if target_arch in ("x86_64", "amd64"): cmd += ' -D_LP64' + elif target_arch == 'aarch64': + cmd += ' -D_LP64 -D__LP64__ -D__aarch64__' else: cmd += ' -D__i386__' - if GetTarget() == 'darwin': + + target = GetTarget() + if target == 'darwin': cmd += ' -D__APPLE__' + elif target == 'android': + cmd += ' -D__ANDROID__' optlevel = GetOptimizeOption(opts) if (optlevel==1): cmd += ' -D_DEBUG' @@ -1744,8 +1754,8 @@ def CompileLink(dll, obj, opts): if LDFLAGS != "": cmd += " " + LDFLAGS - # Don't link libraries with Python. - if "PYTHON" in opts and GetOrigExt(dll) != ".exe" and not RTDIST: + # Don't link libraries with Python, except on Android. + if "PYTHON" in opts and GetOrigExt(dll) != ".exe" and not RTDIST and GetTarget() != 'android': opts = opts[:] opts.remove("PYTHON") diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index b1666aee39..6ea7ec2626 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -37,6 +37,7 @@ TARGET_ARCH = None HAS_TARGET_ARCH = False TOOLCHAIN_PREFIX = "" ANDROID_ABI = None +ANDROID_API = 14 SYS_LIB_DIRS = [] SYS_INC_DIRS = [] DEBUG_DEPENDENCIES = False @@ -290,7 +291,13 @@ def GetHost(): elif sys.platform == 'darwin': return 'darwin' elif sys.platform.startswith('linux'): - return 'linux' + try: + # Python seems to offer no built-in way to check this. + osname = subprocess.check_output(["uname", "-o"]) + if osname.strip().lower() == b'android': + return 'android' + except: + return 'linux' elif sys.platform.startswith('freebsd'): return 'freebsd' else: @@ -344,9 +351,19 @@ def SetTarget(target, arch=None): if arch not in choices: exit('Mac OS X architecture must be one of %s' % (', '.join(choices))) - elif target == 'android': + elif target == 'android' or target.startswith('android-'): if arch is None: - arch = 'arm' + # If compiling on Android, default to same architecture. Otherwise, arm. + if host == 'android': + arch = host_arch + else: + arch = 'arm' + + # Did we specify an API level? + target, _, api = target.partition('-') + if api: + global ANDROID_API + ANDROID_API = int(api) # Determine the prefix for our gcc tools, eg. arm-linux-androideabi-gcc global ANDROID_ABI @@ -356,14 +373,23 @@ def SetTarget(target, arch=None): elif arch == 'arm': ANDROID_ABI = 'armeabi' TOOLCHAIN_PREFIX = 'arm-linux-androideabi-' - elif arch == 'x86': - ANDROID_ABI = 'x86' - TOOLCHAIN_PREFIX = 'i686-linux-android-' + elif arch == 'aarch64': + ANDROID_ABI = 'arm64-v8a' + TOOLCHAIN_PREFIX = 'aarch64-linux-android-' elif arch == 'mips': ANDROID_ABI = 'mips' TOOLCHAIN_PREFIX = 'mipsel-linux-android-' + elif arch == 'mips64': + ANDROID_ABI = 'mips64' + TOOLCHAIN_PREFIX = 'mips64el-linux-android-' + elif arch == 'x86': + ANDROID_ABI = 'x86' + TOOLCHAIN_PREFIX = 'i686-linux-android-' + elif arch == 'x86_64': + ANDROID_ABI = 'x86_64' + TOOLCHAIN_PREFIX = 'x86_64-linux-android-' else: - exit('Android architecture must be arm, armv7a, x86 or mips') + exit('Android architecture must be arm, armv7a, aarch64, mips, mips64, x86 or x86_64') elif target == 'linux': if arch is not None: @@ -413,13 +439,13 @@ def CrossCompiling(): return GetTarget() != GetHost() def GetCC(): - if TARGET == 'darwin' or TARGET == 'freebsd': + if TARGET in ('darwin', 'freebsd', 'android'): return os.environ.get('CC', TOOLCHAIN_PREFIX + 'clang') else: return os.environ.get('CC', TOOLCHAIN_PREFIX + 'gcc') def GetCXX(): - if TARGET == 'darwin' or TARGET == 'freebsd': + if TARGET in ('darwin', 'freebsd', 'android'): return os.environ.get('CXX', TOOLCHAIN_PREFIX + 'clang++') else: return os.environ.get('CXX', TOOLCHAIN_PREFIX + 'g++') @@ -2339,6 +2365,16 @@ def SdkLocateAndroid(): if GetTarget() != 'android': return + # Allow ANDROID_API/ANDROID_ABI to be used in makepanda.py. + api = ANDROID_API + SDK["ANDROID_API"] = api + + abi = ANDROID_ABI + SDK["ANDROID_ABI"] = abi + + if GetHost() == 'android': + return + # Determine the NDK installation directory. if 'NDK_ROOT' not in os.environ: exit('NDK_ROOT must be set when compiling for Android!') @@ -2355,18 +2391,20 @@ def SdkLocateAndroid(): if arch == 'armv7a' or arch == 'arm': arch = 'arm' toolchain = 'arm-linux-androideabi-' + gcc_ver - elif arch == 'x86': - toolchain = 'x86-' + gcc_ver + elif arch == 'aarch64': + toolchain = 'aarch64-linux-android-' + gcc_ver elif arch == 'mips': toolchain = 'mipsel-linux-android-' + gcc_ver + elif arch == 'mips64': + toolchain = 'mips64el-linux-android-' + gcc_ver + elif arch == 'x86': + toolchain = 'x86-' + gcc_ver + elif arch == 'x86_64': + toolchain = 'x86_64-' + gcc_ver SDK["ANDROID_TOOLCHAIN"] = os.path.join(ndk_root, 'toolchains', toolchain) - # Allow ANDROID_ABI to be used in makepanda.py. - abi = ANDROID_ABI - SDK["ANDROID_ABI"] = abi - # Determine the sysroot directory. - SDK["SYSROOT"] = os.path.join(ndk_root, 'platforms', 'android-9', 'arch-%s' % (arch)) + SDK["SYSROOT"] = os.path.join(ndk_root, 'platforms', 'android-%s' % (api), 'arch-%s' % (arch)) #IncDirectory("ALWAYS", os.path.join(SDK["SYSROOT"], 'usr', 'include')) stdlibc = os.path.join(ndk_root, 'sources', 'cxx-stl', 'gnu-libstdc++', gcc_ver) @@ -2626,7 +2664,12 @@ def SetupBuildEnvironment(compiler): print("Using compiler: %s" % compiler) print("Host OS: %s" % GetHost()) print("Host arch: %s" % GetHostArch()) + + target = GetTarget() + if target != 'android': print("Target OS: %s" % GetTarget()) + else: + print("Target OS: %s (API level %d)" % (GetTarget(), ANDROID_API)) print("Target arch: %s" % GetTargetArch()) # Set to English so we can safely parse the result of gcc commands. @@ -2732,7 +2775,7 @@ def SetupBuildEnvironment(compiler): print(" " + dir) # In the case of Android, we have to put the toolchain on the PATH in order to use it. - if GetTarget() == 'android': + if GetTarget() == 'android' and GetHost() != 'android': # Locate the directory where the toolchain binaries reside. prebuilt_dir = os.path.join(SDK['ANDROID_TOOLCHAIN'], 'prebuilt') if not os.path.isdir(prebuilt_dir): From d35e6c5113702c20ae7f9ee8f706d6206378289f Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 28 Jan 2018 14:26:47 +0100 Subject: [PATCH 051/118] android: include android_native_app_glue in repository These files are from the NDK, but don't seem to be included in termux. Since they are two small files that change very rarely, it's easier to just include them in the Panda repo. [skip ci] --- makepanda/makepanda.py | 5 +- panda/src/android/android_native_app_glue.c | 442 ++++++++++++++++++++ panda/src/android/android_native_app_glue.h | 354 ++++++++++++++++ 3 files changed, 798 insertions(+), 3 deletions(-) create mode 100644 panda/src/android/android_native_app_glue.c create mode 100644 panda/src/android/android_native_app_glue.h diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index ef02a1eb04..af552d10b8 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -5058,8 +5058,7 @@ if (not RTDIST and not RUNTIME and PkgSkip("PVIEW")==0 and GetTarget() != 'andro # if (not RUNTIME and GetTarget() == 'android'): - native_app_glue = os.path.join(SDK['ANDROID_NDK'], 'sources', 'android', 'native_app_glue') - OPTS=['DIR:panda/src/android', 'DIR:' + native_app_glue] + OPTS=['DIR:panda/src/android'] TargetAdd('p3android_composite1.obj', opts=OPTS, input='p3android_composite1.cxx') TargetAdd('libp3android.dll', input='p3android_composite1.obj') @@ -5087,7 +5086,7 @@ if (not RUNTIME and GetTarget() == 'android'): if (GetTarget() == 'android' and PkgSkip("EGL")==0 and PkgSkip("GLES")==0 and not RUNTIME): DefSymbol('GLES', 'OPENGLES_1', '') - OPTS=['DIR:panda/src/androiddisplay', 'DIR:panda/src/glstuff', 'DIR:' + native_app_glue, 'BUILDING:PANDAGLES', 'GLES', 'EGL'] + OPTS=['DIR:panda/src/androiddisplay', 'DIR:panda/src/glstuff', 'BUILDING:PANDAGLES', 'GLES', 'EGL'] TargetAdd('pandagles_androiddisplay_composite1.obj', opts=OPTS, input='p3androiddisplay_composite1.cxx') OPTS=['DIR:panda/metalibs/pandagles', 'BUILDING:PANDAGLES', 'GLES', 'EGL'] TargetAdd('pandagles_pandagles.obj', opts=OPTS, input='pandagles.cxx') diff --git a/panda/src/android/android_native_app_glue.c b/panda/src/android/android_native_app_glue.c new file mode 100644 index 0000000000..7eada08666 --- /dev/null +++ b/panda/src/android/android_native_app_glue.c @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include +#include + +#include "android_native_app_glue.h" +#include + +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__)) +#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__)) + +/* For debug builds, always enable the debug traces in this library */ +#ifndef NDEBUG +# define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", __VA_ARGS__)) +#else +# define LOGV(...) ((void)0) +#endif + +static void free_saved_state(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + if (android_app->savedState != NULL) { + free(android_app->savedState); + android_app->savedState = NULL; + android_app->savedStateSize = 0; + } + pthread_mutex_unlock(&android_app->mutex); +} + +int8_t android_app_read_cmd(struct android_app* android_app) { + int8_t cmd; + if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) { + switch (cmd) { + case APP_CMD_SAVE_STATE: + free_saved_state(android_app); + break; + } + return cmd; + } else { + LOGE("No data on command pipe!"); + } + return -1; +} + +static void print_cur_config(struct android_app* android_app) { + char lang[2], country[2]; + AConfiguration_getLanguage(android_app->config, lang); + AConfiguration_getCountry(android_app->config, country); + + LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d " + "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d " + "modetype=%d modenight=%d", + AConfiguration_getMcc(android_app->config), + AConfiguration_getMnc(android_app->config), + lang[0], lang[1], country[0], country[1], + AConfiguration_getOrientation(android_app->config), + AConfiguration_getTouchscreen(android_app->config), + AConfiguration_getDensity(android_app->config), + AConfiguration_getKeyboard(android_app->config), + AConfiguration_getNavigation(android_app->config), + AConfiguration_getKeysHidden(android_app->config), + AConfiguration_getNavHidden(android_app->config), + AConfiguration_getSdkVersion(android_app->config), + AConfiguration_getScreenSize(android_app->config), + AConfiguration_getScreenLong(android_app->config), + AConfiguration_getUiModeType(android_app->config), + AConfiguration_getUiModeNight(android_app->config)); +} + +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_INPUT_CHANGED: + LOGV("APP_CMD_INPUT_CHANGED\n"); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + android_app->inputQueue = android_app->pendingInputQueue; + if (android_app->inputQueue != NULL) { + LOGV("Attaching input queue to looper"); + AInputQueue_attachLooper(android_app->inputQueue, + android_app->looper, LOOPER_ID_INPUT, NULL, + &android_app->inputPollSource); + } + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_INIT_WINDOW: + LOGV("APP_CMD_INIT_WINDOW\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = android_app->pendingWindow; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_TERM_WINDOW: + LOGV("APP_CMD_TERM_WINDOW\n"); + pthread_cond_broadcast(&android_app->cond); + break; + + case APP_CMD_RESUME: + case APP_CMD_START: + case APP_CMD_PAUSE: + case APP_CMD_STOP: + LOGV("activityState=%d\n", cmd); + pthread_mutex_lock(&android_app->mutex); + android_app->activityState = cmd; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_CONFIG_CHANGED: + LOGV("APP_CMD_CONFIG_CHANGED\n"); + AConfiguration_fromAssetManager(android_app->config, + android_app->activity->assetManager); + print_cur_config(android_app); + break; + + case APP_CMD_DESTROY: + LOGV("APP_CMD_DESTROY\n"); + android_app->destroyRequested = 1; + break; + } +} + +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_TERM_WINDOW: + LOGV("APP_CMD_TERM_WINDOW\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = NULL; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_SAVE_STATE: + LOGV("APP_CMD_SAVE_STATE\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->stateSaved = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_RESUME: + free_saved_state(android_app); + break; + } +} + +void app_dummy() { + +} + +static void android_app_destroy(struct android_app* android_app) { + LOGV("android_app_destroy!"); + free_saved_state(android_app); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + AConfiguration_delete(android_app->config); + android_app->destroyed = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + // Can't touch android_app object after this. +} + +static void process_input(struct android_app* app, struct android_poll_source* source) { + AInputEvent* event = NULL; + while (AInputQueue_getEvent(app->inputQueue, &event) >= 0) { + LOGV("New input event: type=%d\n", AInputEvent_getType(event)); + if (AInputQueue_preDispatchEvent(app->inputQueue, event)) { + continue; + } + int32_t handled = 0; + if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event); + AInputQueue_finishEvent(app->inputQueue, event, handled); + } +} + +static void process_cmd(struct android_app* app, struct android_poll_source* source) { + int8_t cmd = android_app_read_cmd(app); + android_app_pre_exec_cmd(app, cmd); + if (app->onAppCmd != NULL) app->onAppCmd(app, cmd); + android_app_post_exec_cmd(app, cmd); +} + +static void* android_app_entry(void* param) { + struct android_app* android_app = (struct android_app*)param; + + android_app->config = AConfiguration_new(); + AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager); + + print_cur_config(android_app); + + android_app->cmdPollSource.id = LOOPER_ID_MAIN; + android_app->cmdPollSource.app = android_app; + android_app->cmdPollSource.process = process_cmd; + android_app->inputPollSource.id = LOOPER_ID_INPUT; + android_app->inputPollSource.app = android_app; + android_app->inputPollSource.process = process_input; + + ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); + ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL, + &android_app->cmdPollSource); + android_app->looper = looper; + + pthread_mutex_lock(&android_app->mutex); + android_app->running = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + + android_main(android_app); + + android_app_destroy(android_app); + return NULL; +} + +// -------------------------------------------------------------------- +// Native activity interaction (called from main thread) +// -------------------------------------------------------------------- + +static struct android_app* android_app_create(ANativeActivity* activity, + void* savedState, size_t savedStateSize) { + struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app)); + memset(android_app, 0, sizeof(struct android_app)); + android_app->activity = activity; + + pthread_mutex_init(&android_app->mutex, NULL); + pthread_cond_init(&android_app->cond, NULL); + + if (savedState != NULL) { + android_app->savedState = malloc(savedStateSize); + android_app->savedStateSize = savedStateSize; + memcpy(android_app->savedState, savedState, savedStateSize); + } + + int msgpipe[2]; + if (pipe(msgpipe)) { + LOGE("could not create pipe: %s", strerror(errno)); + return NULL; + } + android_app->msgread = msgpipe[0]; + android_app->msgwrite = msgpipe[1]; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&android_app->thread, &attr, android_app_entry, android_app); + + // Wait for thread to start. + pthread_mutex_lock(&android_app->mutex); + while (!android_app->running) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + return android_app; +} + +static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { + if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { + LOGE("Failure writing android_app cmd: %s\n", strerror(errno)); + } +} + +static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) { + pthread_mutex_lock(&android_app->mutex); + android_app->pendingInputQueue = inputQueue; + android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED); + while (android_app->inputQueue != android_app->pendingInputQueue) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) { + pthread_mutex_lock(&android_app->mutex); + if (android_app->pendingWindow != NULL) { + android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW); + } + android_app->pendingWindow = window; + if (window != NULL) { + android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW); + } + while (android_app->window != android_app->pendingWindow) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, cmd); + while (android_app->activityState != cmd) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_free(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, APP_CMD_DESTROY); + while (!android_app->destroyed) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + close(android_app->msgread); + close(android_app->msgwrite); + pthread_cond_destroy(&android_app->cond); + pthread_mutex_destroy(&android_app->mutex); + free(android_app); +} + +static void onDestroy(ANativeActivity* activity) { + LOGV("Destroy: %p\n", activity); + android_app_free((struct android_app*)activity->instance); +} + +static void onStart(ANativeActivity* activity) { + LOGV("Start: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START); +} + +static void onResume(ANativeActivity* activity) { + LOGV("Resume: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME); +} + +static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) { + struct android_app* android_app = (struct android_app*)activity->instance; + void* savedState = NULL; + + LOGV("SaveInstanceState: %p\n", activity); + pthread_mutex_lock(&android_app->mutex); + android_app->stateSaved = 0; + android_app_write_cmd(android_app, APP_CMD_SAVE_STATE); + while (!android_app->stateSaved) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + + if (android_app->savedState != NULL) { + savedState = android_app->savedState; + *outLen = android_app->savedStateSize; + android_app->savedState = NULL; + android_app->savedStateSize = 0; + } + + pthread_mutex_unlock(&android_app->mutex); + + return savedState; +} + +static void onPause(ANativeActivity* activity) { + LOGV("Pause: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE); +} + +static void onStop(ANativeActivity* activity) { + LOGV("Stop: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP); +} + +static void onConfigurationChanged(ANativeActivity* activity) { + struct android_app* android_app = (struct android_app*)activity->instance; + LOGV("ConfigurationChanged: %p\n", activity); + android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED); +} + +static void onLowMemory(ANativeActivity* activity) { + struct android_app* android_app = (struct android_app*)activity->instance; + LOGV("LowMemory: %p\n", activity); + android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY); +} + +static void onWindowFocusChanged(ANativeActivity* activity, int focused) { + LOGV("WindowFocusChanged: %p -- %d\n", activity, focused); + android_app_write_cmd((struct android_app*)activity->instance, + focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); +} + +static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { + LOGV("NativeWindowCreated: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, window); +} + +static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { + LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, NULL); +} + +static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { + LOGV("InputQueueCreated: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, queue); +} + +static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { + LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, NULL); +} + +JNIEXPORT +void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, + size_t savedStateSize) { + LOGV("Creating: %p\n", activity); + activity->callbacks->onDestroy = onDestroy; + activity->callbacks->onStart = onStart; + activity->callbacks->onResume = onResume; + activity->callbacks->onSaveInstanceState = onSaveInstanceState; + activity->callbacks->onPause = onPause; + activity->callbacks->onStop = onStop; + activity->callbacks->onConfigurationChanged = onConfigurationChanged; + activity->callbacks->onLowMemory = onLowMemory; + activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; + activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; + activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; + activity->callbacks->onInputQueueCreated = onInputQueueCreated; + activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; + + activity->instance = android_app_create(activity, savedState, savedStateSize); +} diff --git a/panda/src/android/android_native_app_glue.h b/panda/src/android/android_native_app_glue.h new file mode 100644 index 0000000000..c99d6e12af --- /dev/null +++ b/panda/src/android/android_native_app_glue.h @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _ANDROID_NATIVE_APP_GLUE_H +#define _ANDROID_NATIVE_APP_GLUE_H + +#include +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The native activity interface provided by + * is based on a set of application-provided callbacks that will be called + * by the Activity's main thread when certain events occur. + * + * This means that each one of this callbacks _should_ _not_ block, or they + * risk having the system force-close the application. This programming + * model is direct, lightweight, but constraining. + * + * The 'android_native_app_glue' static library is used to provide a different + * execution model where the application can implement its own main event + * loop in a different thread instead. Here's how it works: + * + * 1/ The application must provide a function named "android_main()" that + * will be called when the activity is created, in a new thread that is + * distinct from the activity's main thread. + * + * 2/ android_main() receives a pointer to a valid "android_app" structure + * that contains references to other important objects, e.g. the + * ANativeActivity obejct instance the application is running in. + * + * 3/ the "android_app" object holds an ALooper instance that already + * listens to two important things: + * + * - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX + * declarations below. + * + * - input events coming from the AInputQueue attached to the activity. + * + * Each of these correspond to an ALooper identifier returned by + * ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT, + * respectively. + * + * Your application can use the same ALooper to listen to additional + * file-descriptors. They can either be callback based, or with return + * identifiers starting with LOOPER_ID_USER. + * + * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event, + * the returned data will point to an android_poll_source structure. You + * can call the process() function on it, and fill in android_app->onAppCmd + * and android_app->onInputEvent to be called for your own processing + * of the event. + * + * Alternatively, you can call the low-level functions to read and process + * the data directly... look at the process_cmd() and process_input() + * implementations in the glue to see how to do this. + * + * See the sample named "native-activity" that comes with the NDK with a + * full usage example. Also look at the JavaDoc of NativeActivity. + */ + +struct android_app; + +/** + * Data associated with an ALooper fd that will be returned as the "outData" + * when that source has data ready. + */ +struct android_poll_source { + // The identifier of this source. May be LOOPER_ID_MAIN or + // LOOPER_ID_INPUT. + int32_t id; + + // The android_app this ident is associated with. + struct android_app* app; + + // Function to call to perform the standard processing of data from + // this source. + void (*process)(struct android_app* app, struct android_poll_source* source); +}; + +/** + * This is the interface for the standard glue code of a threaded + * application. In this model, the application's code is running + * in its own thread separate from the main thread of the process. + * It is not required that this thread be associated with the Java + * VM, although it will need to be in order to make JNI calls any + * Java objects. + */ +struct android_app { + // The application can place a pointer to its own state object + // here if it likes. + void* userData; + + // Fill this in with the function to process main app commands (APP_CMD_*) + void (*onAppCmd)(struct android_app* app, int32_t cmd); + + // Fill this in with the function to process input events. At this point + // the event has already been pre-dispatched, and it will be finished upon + // return. Return 1 if you have handled the event, 0 for any default + // dispatching. + int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event); + + // The ANativeActivity object instance that this app is running in. + ANativeActivity* activity; + + // The current configuration the app is running in. + AConfiguration* config; + + // This is the last instance's saved state, as provided at creation time. + // It is NULL if there was no state. You can use this as you need; the + // memory will remain around until you call android_app_exec_cmd() for + // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL. + // These variables should only be changed when processing a APP_CMD_SAVE_STATE, + // at which point they will be initialized to NULL and you can malloc your + // state and place the information here. In that case the memory will be + // freed for you later. + void* savedState; + size_t savedStateSize; + + // The ALooper associated with the app's thread. + ALooper* looper; + + // When non-NULL, this is the input queue from which the app will + // receive user input events. + AInputQueue* inputQueue; + + // When non-NULL, this is the window surface that the app can draw in. + ANativeWindow* window; + + // Current content rectangle of the window; this is the area where the + // window's content should be placed to be seen by the user. + ARect contentRect; + + // Current state of the app's activity. May be either APP_CMD_START, + // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. + int activityState; + + // This is non-zero when the application's NativeActivity is being + // destroyed and waiting for the app thread to complete. + int destroyRequested; + + // ------------------------------------------------- + // Below are "private" implementation of the glue code. + + pthread_mutex_t mutex; + pthread_cond_t cond; + + int msgread; + int msgwrite; + + pthread_t thread; + + struct android_poll_source cmdPollSource; + struct android_poll_source inputPollSource; + + int running; + int stateSaved; + int destroyed; + int redrawNeeded; + AInputQueue* pendingInputQueue; + ANativeWindow* pendingWindow; + ARect pendingContentRect; +}; + +enum { + /** + * Looper data ID of commands coming from the app's main thread, which + * is returned as an identifier from ALooper_pollOnce(). The data for this + * identifier is a pointer to an android_poll_source structure. + * These can be retrieved and processed with android_app_read_cmd() + * and android_app_exec_cmd(). + */ + LOOPER_ID_MAIN = 1, + + /** + * Looper data ID of events coming from the AInputQueue of the + * application's window, which is returned as an identifier from + * ALooper_pollOnce(). The data for this identifier is a pointer to an + * android_poll_source structure. These can be read via the inputQueue + * object of android_app. + */ + LOOPER_ID_INPUT = 2, + + /** + * Start of user-defined ALooper identifiers. + */ + LOOPER_ID_USER = 3, +}; + +enum { + /** + * Command from main thread: the AInputQueue has changed. Upon processing + * this command, android_app->inputQueue will be updated to the new queue + * (or NULL). + */ + APP_CMD_INPUT_CHANGED, + + /** + * Command from main thread: a new ANativeWindow is ready for use. Upon + * receiving this command, android_app->window will contain the new window + * surface. + */ + APP_CMD_INIT_WINDOW, + + /** + * Command from main thread: the existing ANativeWindow needs to be + * terminated. Upon receiving this command, android_app->window still + * contains the existing window; after calling android_app_exec_cmd + * it will be set to NULL. + */ + APP_CMD_TERM_WINDOW, + + /** + * Command from main thread: the current ANativeWindow has been resized. + * Please redraw with its new size. + */ + APP_CMD_WINDOW_RESIZED, + + /** + * Command from main thread: the system needs that the current ANativeWindow + * be redrawn. You should redraw the window before handing this to + * android_app_exec_cmd() in order to avoid transient drawing glitches. + */ + APP_CMD_WINDOW_REDRAW_NEEDED, + + /** + * Command from main thread: the content area of the window has changed, + * such as from the soft input window being shown or hidden. You can + * find the new content rect in android_app::contentRect. + */ + APP_CMD_CONTENT_RECT_CHANGED, + + /** + * Command from main thread: the app's activity window has gained + * input focus. + */ + APP_CMD_GAINED_FOCUS, + + /** + * Command from main thread: the app's activity window has lost + * input focus. + */ + APP_CMD_LOST_FOCUS, + + /** + * Command from main thread: the current device configuration has changed. + */ + APP_CMD_CONFIG_CHANGED, + + /** + * Command from main thread: the system is running low on memory. + * Try to reduce your memory use. + */ + APP_CMD_LOW_MEMORY, + + /** + * Command from main thread: the app's activity has been started. + */ + APP_CMD_START, + + /** + * Command from main thread: the app's activity has been resumed. + */ + APP_CMD_RESUME, + + /** + * Command from main thread: the app should generate a new saved state + * for itself, to restore from later if needed. If you have saved state, + * allocate it with malloc and place it in android_app.savedState with + * the size in android_app.savedStateSize. The will be freed for you + * later. + */ + APP_CMD_SAVE_STATE, + + /** + * Command from main thread: the app's activity has been paused. + */ + APP_CMD_PAUSE, + + /** + * Command from main thread: the app's activity has been stopped. + */ + APP_CMD_STOP, + + /** + * Command from main thread: the app's activity is being destroyed, + * and waiting for the app thread to clean up and exit before proceeding. + */ + APP_CMD_DESTROY, +}; + +/** + * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next + * app command message. + */ +int8_t android_app_read_cmd(struct android_app* android_app); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * initial pre-processing of the given command. You can perform your own + * actions for the command after calling this function. + */ +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * final post-processing of the given command. You must have done your own + * actions for the command before calling this function. + */ +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * Dummy function that used to be used to prevent the linker from stripping app + * glue code. No longer necessary, since __attribute__((visibility("default"))) + * does this for us. + */ +__attribute__(( + deprecated("Calls to app_dummy are no longer necessary. See " + "https://github.com/android-ndk/ndk/issues/381."))) void +app_dummy(); + +/** + * This is the function that application code must implement, representing + * the main entry to the app. + */ +extern void android_main(struct android_app* app); + +#ifdef __cplusplus +} +#endif + +#endif /* _ANDROID_NATIVE_APP_GLUE_H */ From 002b37336121a81c1b211a022188e6e268b9c6bb Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 28 Jan 2018 14:44:12 +0100 Subject: [PATCH 052/118] makepanda: fix GetHost() on linux [skip ci] --- makepanda/makepandacore.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 6ea7ec2626..8c989f3d2c 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -296,6 +296,8 @@ def GetHost(): osname = subprocess.check_output(["uname", "-o"]) if osname.strip().lower() == b'android': return 'android' + else: + return 'linux' except: return 'linux' elif sys.platform.startswith('freebsd'): From e301e5fe5ecc66fd3fa187d055d018139dd496b8 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 29 Jan 2018 19:36:51 +0100 Subject: [PATCH 053/118] android: fix various compilation errors --- dtool/src/dtoolutil/filename.cxx | 5 +++++ panda/src/gobj/vertexDataSaveFile.cxx | 5 +++++ panda/src/net/connectionManager.cxx | 4 ++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/dtool/src/dtoolutil/filename.cxx b/dtool/src/dtoolutil/filename.cxx index c862c0aabf..d2c566b557 100644 --- a/dtool/src/dtoolutil/filename.cxx +++ b/dtool/src/dtoolutil/filename.cxx @@ -48,6 +48,11 @@ #include #endif +#if defined(__ANDROID__) && !defined(HAVE_LOCKF) +// Needed for flock. +#include +#endif + TextEncoder::Encoding Filename::_filesystem_encoding = TextEncoder::E_utf8; TVOLATILE AtomicAdjust::Pointer Filename::_home_directory; diff --git a/panda/src/gobj/vertexDataSaveFile.cxx b/panda/src/gobj/vertexDataSaveFile.cxx index 0aa8abff56..fd7087ba5d 100644 --- a/panda/src/gobj/vertexDataSaveFile.cxx +++ b/panda/src/gobj/vertexDataSaveFile.cxx @@ -23,6 +23,11 @@ #include #endif // _WIN32 +#if defined(__ANDROID__) && !defined(HAVE_LOCKF) +// Needed for flock. +#include +#endif + /** * */ diff --git a/panda/src/net/connectionManager.cxx b/panda/src/net/connectionManager.cxx index f7fdbe7e3e..8a0bfc1e86 100644 --- a/panda/src/net/connectionManager.cxx +++ b/panda/src/net/connectionManager.cxx @@ -26,7 +26,7 @@ #elif defined(WIN32_VC) || defined(WIN64_VC) #include // For gethostname() #include // For GetAdaptersAddresses() -#elif defined(ANDROID) +#elif defined(__ANDROID__) #include #else #include @@ -538,7 +538,7 @@ scan_interfaces() { PANDA_FREE_ARRAY(addresses); } -#elif defined(ANDROID) +#elif defined(__ANDROID__) // TODO: implementation using netlink_socket? #else // WIN32_VC From 7c1dd4050bf8fa20bd8a5d4466cb0be665b14e33 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 29 Jan 2018 19:38:46 +0100 Subject: [PATCH 054/118] android: change assets mount dir, point binary path to .apk The binary path we get from /proc/self/exe isn't very useful; the path to the .apk is barely more useful but it still doesn't make a whole lot of sense. It might make more sense to set it to the path of the native .so that is being loaded by NativeActivity. --- dtool/src/dtoolutil/executionEnvironment.cxx | 5 +-- makepanda/makepanda.py | 2 +- panda/src/android/android_main.cxx | 45 +++++++++++++------- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/dtool/src/dtoolutil/executionEnvironment.cxx b/dtool/src/dtoolutil/executionEnvironment.cxx index ad782b521e..c4f2e5093e 100644 --- a/dtool/src/dtoolutil/executionEnvironment.cxx +++ b/dtool/src/dtoolutil/executionEnvironment.cxx @@ -540,7 +540,6 @@ read_environment_variables() { */ void ExecutionEnvironment:: read_args() { -#ifndef ANDROID // First, we need to fill in _dtool_name. This contains the full path to // the p3dtool library. @@ -578,7 +577,7 @@ read_args() { } #endif -#if defined(IS_FREEBSD) || defined(IS_LINUX) +#if defined(IS_FREEBSD) || (defined(IS_LINUX) && !defined(__ANDROID__)) // FreeBSD and Linux have a function to get the origin of a loaded library. char origin[PATH_MAX + 1]; @@ -833,8 +832,6 @@ read_args() { } #endif // _WIN32 -#endif // ANDROID - if (_dtool_name.empty()) { _dtool_name = _binary_name; } diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index af552d10b8..c3856fcaed 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -2353,7 +2353,7 @@ def WriteConfigSettings(): dtool_config["HAVE_CGGL"] = '1' dtool_config["HAVE_CGDX9"] = '1' - if (GetTarget() != "linux"): + if GetTarget() not in ("linux", "android"): dtool_config["HAVE_PROC_SELF_EXE"] = 'UNDEF' dtool_config["HAVE_PROC_SELF_MAPS"] = 'UNDEF' dtool_config["HAVE_PROC_SELF_CMDLINE"] = 'UNDEF' diff --git a/panda/src/android/android_main.cxx b/panda/src/android/android_main.cxx index ebc777ffa3..6626817410 100644 --- a/panda/src/android/android_main.cxx +++ b/panda/src/android/android_main.cxx @@ -52,43 +52,58 @@ void android_main(struct android_app* app) { // Fetch the path to the data directory. jfieldID datadir_field = env->GetFieldID(appinfo_class, "dataDir", "Ljava/lang/String;"); jstring datadir = (jstring) env->GetObjectField(appinfo, datadir_field); - const char *data_path = env->GetStringUTFChars(datadir, NULL); + const char *data_path = env->GetStringUTFChars(datadir, nullptr); - Filename::_internal_data_dir = data_path; - android_cat.info() << "Path to data: " << data_path << "\n"; + if (data_path != nullptr) { + Filename::_internal_data_dir = data_path; + android_cat.info() << "Path to data: " << data_path << "\n"; - env->ReleaseStringUTFChars(datadir, data_path); + env->ReleaseStringUTFChars(datadir, data_path); + } // Fetch the path to the library directory. - jfieldID libdir_field = env->GetFieldID(appinfo_class, "nativeLibraryDir", "Ljava/lang/String;"); - jstring libdir = (jstring) env->GetObjectField(appinfo, libdir_field); - const char *lib_path = env->GetStringUTFChars(libdir, NULL); + if (ExecutionEnvironment::get_dtool_name().empty()) { + jfieldID libdir_field = env->GetFieldID(appinfo_class, "nativeLibraryDir", "Ljava/lang/String;"); + jstring libdir = (jstring) env->GetObjectField(appinfo, libdir_field); + const char *lib_path = env->GetStringUTFChars(libdir, nullptr); - string dtool_name = string(lib_path) + "/libp3dtool.so"; - ExecutionEnvironment::set_dtool_name(dtool_name); - android_cat.info() << "Path to dtool: " << dtool_name << "\n"; + if (lib_path != nullptr) { + string dtool_name = string(lib_path) + "/libp3dtool.so"; + ExecutionEnvironment::set_dtool_name(dtool_name); + android_cat.info() << "Path to dtool: " << dtool_name << "\n"; - env->ReleaseStringUTFChars(libdir, lib_path); + env->ReleaseStringUTFChars(libdir, lib_path); + } + } // Get the path to the APK. jmethodID methodID = env->GetMethodID(activity_class, "getPackageCodePath", "()Ljava/lang/String;"); jstring code_path = (jstring) env->CallObjectMethod(activity->clazz, methodID); const char* apk_path; - apk_path = env->GetStringUTFChars(code_path, NULL); + apk_path = env->GetStringUTFChars(code_path, nullptr); + + // We're going to set this as binary name, which is better than the + // default (which refers to the zygote). Or should we set it to the + // native library? How do we get the path to that? android_cat.info() << "Path to APK: " << apk_path << "\n"; + ExecutionEnvironment::set_binary_name(apk_path); // Mount the assets directory. + Filename apk_fn(apk_path); PT(VirtualFileMountAndroidAsset) asset_mount; - asset_mount = new VirtualFileMountAndroidAsset(app->activity->assetManager, apk_path); + asset_mount = new VirtualFileMountAndroidAsset(app->activity->assetManager, apk_fn); VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - vfs->mount(asset_mount, "/android_asset", 0); + + Filename asset_dir(apk_fn.get_dirname(), "assets"); + vfs->mount(asset_mount, asset_dir, 0); // Release the apk_path. env->ReleaseStringUTFChars(code_path, apk_path); // Now add the asset directory to the model-path. - get_model_path().append_directory("/android_asset"); + //TODO: prevent it from adding the directory multiple times. + get_model_path().append_directory(asset_dir); // Create bogus argc and argv, then call our main function. char *argv[] = {NULL}; From e04cb17a30e88b4631f1817f0d218f91b400c439 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 29 Jan 2018 19:57:01 +0100 Subject: [PATCH 055/118] makepanda: change Android build model We no longer copy libs to a separate libs dir to entertain Ant (which is no longer the build system of choice on Android). Also, rather than copying the Java sources to built/src, we now compile them and put the classes in built/classes. Furthermore, executables are really compiled as executables now (rather than as libraries) to allow building and running Panda in termux. --- makepanda/makepanda.py | 68 ++++++++++++++++++++++---------------- makepanda/makepandacore.py | 26 ++------------- 2 files changed, 43 insertions(+), 51 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index c3856fcaed..b00c947208 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -943,7 +943,7 @@ if (COMPILER=="GCC"): if GetTarget() == 'android': LibName("ALWAYS", '-llog') - LibName("ALWAYS", '-landroid') + LibName("ANDROID", '-landroid') LibName("JNIGRAPHICS", '-ljnigraphics') for pkg in MAYAVERSIONS: @@ -1704,8 +1704,11 @@ def CompileLink(dll, obj, opts): if COMPILER == "GCC": cxx = GetCXX() - if GetOrigExt(dll) == ".exe" and GetTarget() != 'android': + if GetOrigExt(dll) == ".exe": cmd = cxx + ' -o ' + dll + ' -L' + GetOutputDir() + '/lib -L' + GetOutputDir() + '/tmp' + if GetTarget() == "android": + # Necessary to work around an issue with libandroid depending on vendor libraries + cmd += ' -Wl,--allow-shlib-undefined' else: if (GetTarget() == "darwin"): cmd = cxx + ' -undefined dynamic_lookup' @@ -1718,6 +1721,7 @@ def CompileLink(dll, obj, opts): cmd += ' -o ' + dll + ' -L' + GetOutputDir() + '/lib -L' + GetOutputDir() + '/tmp' else: cmd = cxx + ' -shared' + # Always set soname on Android to avoid a linker warning when loading the library. if "MODULE" not in opts or GetTarget() == 'android': cmd += " -Wl,-soname=" + os.path.basename(dll) cmd += ' -o ' + dll + ' -L' + GetOutputDir() + '/lib -L' + GetOutputDir() + '/tmp' @@ -1774,14 +1778,7 @@ def CompileLink(dll, obj, opts): oscmd(cmd) - if GetTarget() == 'android': - # Copy the library to built/libs/$ANDROID_ABI and strip it. - # This is the format that Android NDK projects should use. - new_path = '%s/libs/%s/%s' % (GetOutputDir(), SDK["ANDROID_ABI"], os.path.basename(dll)) - CopyFile(new_path, dll) - oscmd('%s --strip-unneeded %s' % (GetStrip(), BracketNameWithQuotes(new_path))) - - elif (GetOptimizeOption(opts)==4 and GetTarget() == 'linux'): + if GetOptimizeOption(opts) == 4 and GetTarget() in ('linux', 'android'): oscmd(GetStrip() + " --strip-unneeded " + BracketNameWithQuotes(dll)) os.system("chmod +x " + BracketNameWithQuotes(dll)) @@ -1890,6 +1887,25 @@ def CompileRsrc(target, src, opts): cmd += " " + BracketNameWithQuotes(src) oscmd(cmd) +########################################################################################## +# +# CompileJava (Android only) +# +########################################################################################## + +def CompileJava(target, src, opts): + """Compiles a .java file into a .class file.""" + cmd = "ecj " + + optlevel = GetOptimizeOption(opts) + if optlevel >= 4: + cmd += "-debug:none " + + cmd += "-cp " + GetOutputDir() + "/classes " + cmd += "-d " + GetOutputDir() + "/classes " + cmd += BracketNameWithQuotes(src) + oscmd(cmd) + ########################################################################################## # # FreezePy @@ -2143,6 +2159,9 @@ def CompileAnything(target, inputs, opts, progress = None): elif (origsuffix==".rsrc"): ProgressOutput(progress, "Building resource object", target) return CompileRsrc(target, infile, opts) + elif (origsuffix==".class"): + ProgressOutput(progress, "Building Java class", target) + return CompileJava(target, infile, opts) elif (origsuffix==".obj"): if (infile.endswith(".cxx")): ProgressOutput(progress, "Building C++ object", target) @@ -3233,15 +3252,6 @@ if (PkgSkip("CONTRIB")==0): CopyAllHeaders('contrib/src/contribbase') CopyAllHeaders('contrib/src/ai') -######################################################################## -# -# Copy Java files, if applicable -# -######################################################################## - -if GetTarget() == 'android': - CopyAllJavaSources('panda/src/android') - ######################################################################## # # These definitions are syntactic shorthand. They make it easy @@ -3561,7 +3571,7 @@ TargetAdd('libpandaexpress.dll', input='p3express_composite1.obj') TargetAdd('libpandaexpress.dll', input='p3express_composite2.obj') TargetAdd('libpandaexpress.dll', input='p3pandabase_pandabase.obj') TargetAdd('libpandaexpress.dll', input=COMMON_DTOOL_LIBS) -TargetAdd('libpandaexpress.dll', opts=['ADVAPI', 'WINSOCK2', 'OPENSSL', 'ZLIB', 'WINGDI', 'WINUSER']) +TargetAdd('libpandaexpress.dll', opts=['ADVAPI', 'WINSOCK2', 'OPENSSL', 'ZLIB', 'WINGDI', 'WINUSER', 'ANDROID']) # # DIRECTORY: panda/src/pipeline/ @@ -5059,6 +5069,8 @@ if (not RTDIST and not RUNTIME and PkgSkip("PVIEW")==0 and GetTarget() != 'andro if (not RUNTIME and GetTarget() == 'android'): OPTS=['DIR:panda/src/android'] + TargetAdd('org/panda3d/android/NativeIStream.class', opts=OPTS, input='NativeIStream.java') + TargetAdd('org/panda3d/android/PandaActivity.class', opts=OPTS, input='PandaActivity.java', dep='org/panda3d/android/NativeIStream.class') TargetAdd('p3android_composite1.obj', opts=OPTS, input='p3android_composite1.cxx') TargetAdd('libp3android.dll', input='p3android_composite1.obj') @@ -5070,15 +5082,15 @@ if (not RUNTIME and GetTarget() == 'android'): if (not RTDIST and PkgSkip("PVIEW")==0): TargetAdd('pview_pview.obj', opts=OPTS, input='pview.cxx') - TargetAdd('pview.exe', input='android_native_app_glue.obj') - TargetAdd('pview.exe', input='android_main.obj') - TargetAdd('pview.exe', input='pview_pview.obj') - TargetAdd('pview.exe', input='libp3framework.dll') + TargetAdd('libpview.dll', input='android_native_app_glue.obj') + TargetAdd('libpview.dll', input='android_main.obj') + TargetAdd('libpview.dll', input='pview_pview.obj') + TargetAdd('libpview.dll', input='libp3framework.dll') if not PkgSkip("EGG"): - TargetAdd('pview.exe', input='libpandaegg.dll') - TargetAdd('pview.exe', input='libp3android.dll') - TargetAdd('pview.exe', input=COMMON_PANDA_LIBS) - TargetAdd('AndroidManifest.xml', opts=OPTS, input='pview_manifest.xml') + TargetAdd('libpview.dll', input='libpandaegg.dll') + TargetAdd('libpview.dll', input='libp3android.dll') + TargetAdd('libpview.dll', input=COMMON_PANDA_LIBS) + TargetAdd('libpview.dll', opts=['MODULE', 'ANDROID']) # # DIRECTORY: panda/src/androiddisplay/ diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 8c989f3d2c..75211d3979 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -1145,12 +1145,7 @@ def MakeBuildTree(): MakeDirectory(OUTPUTDIR + "/Frameworks") elif GetTarget() == 'android': - MakeDirectory(OUTPUTDIR + "/libs") - MakeDirectory(OUTPUTDIR + "/libs/" + ANDROID_ABI) - MakeDirectory(OUTPUTDIR + "/src") - MakeDirectory(OUTPUTDIR + "/src/org") - MakeDirectory(OUTPUTDIR + "/src/org/panda3d") - MakeDirectory(OUTPUTDIR + "/src/org/panda3d/android") + MakeDirectory(OUTPUTDIR + "/classes") ######################################################################## # @@ -2892,14 +2887,6 @@ def CopyAllHeaders(dir, skip=[]): WriteBinaryFile(dstfile, ReadBinaryFile(srcfile)) JustBuilt([dstfile], [srcfile]) -def CopyAllJavaSources(dir, skip=[]): - for filename in GetDirectoryContents(dir, ["*.java"], skip): - srcfile = dir + "/" + filename - dstfile = OUTPUTDIR + "/src/org/panda3d/android/" + filename - if (NeedsBuild([dstfile], [srcfile])): - WriteBinaryFile(dstfile, ReadBinaryFile(srcfile)) - JustBuilt([dstfile], [srcfile]) - def CopyTree(dstdir, srcdir, omitVCS=True): if os.path.isdir(dstdir): source_entries = os.listdir(srcdir) @@ -3147,6 +3134,7 @@ def CalcLocation(fn, ipath): if fn.startswith("panda3d/") and fn.endswith(".py"): return OUTPUTDIR + "/" + fn + if (fn.endswith(".class")):return OUTPUTDIR+"/classes/"+fn if (fn.count("/")): return fn dllext = "" target = GetTarget() @@ -3162,6 +3150,7 @@ def CalcLocation(fn, ipath): if (fn.endswith(".lxx")): return CxxFindSource(fn, ipath) if (fn.endswith(".pdef")):return CxxFindSource(fn, ipath) if (fn.endswith(".xml")): return CxxFindSource(fn, ipath) + if (fn.endswith(".java")):return CxxFindSource(fn, ipath) if (fn.endswith(".egg")): return OUTPUTDIR+"/models/"+fn if (fn.endswith(".egg.pz")):return OUTPUTDIR+"/models/"+fn if (fn.endswith(".pyd")): return OUTPUTDIR+"/panda3d/"+fn[:-4]+GetExtensionSuffix() @@ -3197,15 +3186,6 @@ def CalcLocation(fn, ipath): if (fn.endswith(".rsrc")): return OUTPUTDIR+"/tmp/"+fn if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn if (fn.endswith(".app")): return OUTPUTDIR+"/bin/"+fn - elif (target == 'android'): - # On Android, we build the libraries into built/tmp, then copy them. - if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o" - if (fn.endswith(".dll")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".so" - if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn - if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn[:-7]+dllext+".so" - if (fn.endswith(".exe")): return OUTPUTDIR+"/tmp/lib"+fn[:-4]+".so" - if (fn.endswith(".lib")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a" - if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a" else: if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o" if (fn.endswith(".dll")): return OUTPUTDIR+"/lib/"+fn[:-4]+".so" From 0fa8980be2fd01af38f446201ef0b97603c05319 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 29 Jan 2018 20:04:14 +0100 Subject: [PATCH 056/118] android: fix activity lifecycle issues (froze upon quit or resume) It seems Android doesn't want an application to just quit on its own accord; after main() returns we need to kindly request Android to finish the activity and keep handling events until Android destroys the app. --- panda/src/android/android_main.cxx | 45 +++++++++++++++++-- .../androiddisplay/androidGraphicsWindow.cxx | 7 +++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/panda/src/android/android_main.cxx b/panda/src/android/android_main.cxx index 6626817410..32c692489f 100644 --- a/panda/src/android/android_main.cxx +++ b/panda/src/android/android_main.cxx @@ -105,10 +105,49 @@ void android_main(struct android_app* app) { //TODO: prevent it from adding the directory multiple times. get_model_path().append_directory(asset_dir); - // Create bogus argc and argv, then call our main function. - char *argv[] = {NULL}; + // Create bogus argc and argv for calling the main function. + char *argv[] = {nullptr}; int argc = 0; - main(argc, argv); + + while (!app->destroyRequested) { + // Call the main function. This will not return until the app is done. + android_cat.info() << "Calling main()\n"; + main(argc, argv); + + if (app->destroyRequested) { + // The app closed responding to a destroy request. + break; + } + + // Ask Android to clean up the activity. + android_cat.info() << "Exited from main(), finishing activity\n"; + ANativeActivity_finish(activity); + + // We still need to keep an event loop going until Android gives us leave + // to end the process. + int looper_id; + int events; + struct android_poll_source *source; + while ((looper_id = ALooper_pollAll(-1, nullptr, &events, (void**)&source)) >= 0) { + // Process this event, but intercept application command events. + if (looper_id == LOOPER_ID_MAIN) { + int8_t cmd = android_app_read_cmd(app); + android_app_pre_exec_cmd(app, cmd); + android_app_post_exec_cmd(app, cmd); + + // I don't think we can get a resume command after we call finish(), + // but let's handle it just in case. + if (cmd == APP_CMD_RESUME || + cmd == APP_CMD_DESTROY) { + break; + } + } else if (source != nullptr) { + source->process(app, source); + } + } + } + + android_cat.info() << "Destroy requested, exiting from android_main\n"; // Detach the thread before exiting. activity->vm->DetachCurrentThread(); diff --git a/panda/src/androiddisplay/androidGraphicsWindow.cxx b/panda/src/androiddisplay/androidGraphicsWindow.cxx index 946e602aeb..ba875fb46d 100644 --- a/panda/src/androiddisplay/androidGraphicsWindow.cxx +++ b/panda/src/androiddisplay/androidGraphicsWindow.cxx @@ -246,6 +246,13 @@ close_window() { } GraphicsWindow::close_window(); + + nassertv(_app != nullptr); + if (_app->userData == this) { + _app->userData = nullptr; + _app->onAppCmd = nullptr; + _app->onInputEvent = nullptr; + } } /** From 18afcdbd0ab33e355526f036ffe96d790149b6d2 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 29 Jan 2018 20:09:39 +0100 Subject: [PATCH 057/118] android: handle right mouse button, more external keyboard keys --- .../androiddisplay/androidGraphicsWindow.cxx | 88 +++++++++++++++++-- .../androiddisplay/androidGraphicsWindow.h | 2 + 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/panda/src/androiddisplay/androidGraphicsWindow.cxx b/panda/src/androiddisplay/androidGraphicsWindow.cxx index ba875fb46d..d459f56339 100644 --- a/panda/src/androiddisplay/androidGraphicsWindow.cxx +++ b/panda/src/androiddisplay/androidGraphicsWindow.cxx @@ -44,7 +44,8 @@ AndroidGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, int flags, GraphicsStateGuardian *gsg, GraphicsOutput *host) : - GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host) + GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host), + _mouse_button_state(0) { AndroidGraphicsPipe *android_pipe; DCAST_INTO_V(android_pipe, _pipe); @@ -533,10 +534,29 @@ handle_motion_event(const AInputEvent *event) { int32_t action = AMotionEvent_getAction(event); action &= AMOTION_EVENT_ACTION_MASK; - if (action == AMOTION_EVENT_ACTION_DOWN) { - _input_devices[0].button_down(MouseButton::one()); - } else if (action == AMOTION_EVENT_ACTION_UP) { - _input_devices[0].button_up(MouseButton::one()); + if (action == AMOTION_EVENT_ACTION_DOWN || + action == AMOTION_EVENT_ACTION_UP) { + // The up event doesn't let us know which button is up, so we need to + // keep track of the button state ourselves. + int32_t button_state = AMotionEvent_getButtonState(event); + int32_t changed = _mouse_button_state ^ button_state; + if (changed != 0) { + if (changed & AMOTION_EVENT_BUTTON_PRIMARY) { + if (button_state & AMOTION_EVENT_BUTTON_PRIMARY) { + _input_devices[0].button_down(MouseButton::one()); + } else { + _input_devices[0].button_up(MouseButton::one()); + } + } + if (changed & AMOTION_EVENT_BUTTON_SECONDARY) { + if (button_state & AMOTION_EVENT_BUTTON_SECONDARY) { + _input_devices[0].button_down(MouseButton::three()); + } else { + _input_devices[0].button_up(MouseButton::three()); + } + } + _mouse_button_state = button_state; + } } float x = AMotionEvent_getX(event, 0) - _app->contentRect.left; @@ -675,7 +695,7 @@ map_button(int32_t keycode) { case AKEYCODE_ENTER: return KeyboardButton::enter(); case AKEYCODE_DEL: - return KeyboardButton::del(); + return KeyboardButton::backspace(); case AKEYCODE_GRAVE: return KeyboardButton::ascii_key('`'); case AKEYCODE_MINUS: @@ -703,6 +723,7 @@ map_button(int32_t keycode) { case AKEYCODE_PLUS: return KeyboardButton::ascii_key('+'); case AKEYCODE_MENU: + return KeyboardButton::menu(); case AKEYCODE_NOTIFICATION: case AKEYCODE_SEARCH: case AKEYCODE_MEDIA_PLAY_PAUSE: @@ -734,6 +755,61 @@ map_button(int32_t keycode) { case AKEYCODE_BUTTON_START: case AKEYCODE_BUTTON_SELECT: case AKEYCODE_BUTTON_MODE: + break; + case AKEYCODE_ESCAPE: + return KeyboardButton::escape(); + case AKEYCODE_FORWARD_DEL: + return KeyboardButton::del(); + case AKEYCODE_CTRL_LEFT: + return KeyboardButton::lcontrol(); + case AKEYCODE_CTRL_RIGHT: + return KeyboardButton::rcontrol(); + case AKEYCODE_CAPS_LOCK: + return KeyboardButton::caps_lock(); + case AKEYCODE_SCROLL_LOCK: + return KeyboardButton::scroll_lock(); + case AKEYCODE_META_LEFT: + return KeyboardButton::lmeta(); + case AKEYCODE_META_RIGHT: + return KeyboardButton::rmeta(); + case AKEYCODE_FUNCTION: + break; + case AKEYCODE_SYSRQ: + return KeyboardButton::print_screen(); + case AKEYCODE_BREAK: + return KeyboardButton::pause(); + case AKEYCODE_MOVE_HOME: + return KeyboardButton::home(); + case AKEYCODE_MOVE_END: + return KeyboardButton::end(); + case AKEYCODE_INSERT: + return KeyboardButton::insert(); + case AKEYCODE_F1: + return KeyboardButton::f1(); + case AKEYCODE_F2: + return KeyboardButton::f2(); + case AKEYCODE_F3: + return KeyboardButton::f3(); + case AKEYCODE_F4: + return KeyboardButton::f4(); + case AKEYCODE_F5: + return KeyboardButton::f5(); + case AKEYCODE_F6: + return KeyboardButton::f6(); + case AKEYCODE_F7: + return KeyboardButton::f7(); + case AKEYCODE_F8: + return KeyboardButton::f8(); + case AKEYCODE_F9: + return KeyboardButton::f9(); + case AKEYCODE_F10: + return KeyboardButton::f10(); + case AKEYCODE_F11: + return KeyboardButton::f11(); + case AKEYCODE_F12: + return KeyboardButton::f12(); + case AKEYCODE_NUM_LOCK: + return KeyboardButton::num_lock(); default: break; } diff --git a/panda/src/androiddisplay/androidGraphicsWindow.h b/panda/src/androiddisplay/androidGraphicsWindow.h index 10ce6de983..3d1ca79946 100644 --- a/panda/src/androiddisplay/androidGraphicsWindow.h +++ b/panda/src/androiddisplay/androidGraphicsWindow.h @@ -71,6 +71,8 @@ private: EGLDisplay _egl_display; EGLSurface _egl_surface; + int32_t _mouse_button_state; + const ARect *rect; public: From b562aba4e0f423446b8d269929b004591d4b6998 Mon Sep 17 00:00:00 2001 From: nosyliam Date: Fri, 2 Feb 2018 13:40:11 -0800 Subject: [PATCH 058/118] Support static linking with FMOD Closes #241 --- panda/src/audiotraits/config_fmodAudio.cxx | 2 ++ panda/src/audiotraits/config_fmodAudio.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/panda/src/audiotraits/config_fmodAudio.cxx b/panda/src/audiotraits/config_fmodAudio.cxx index 0726118121..affc593636 100644 --- a/panda/src/audiotraits/config_fmodAudio.cxx +++ b/panda/src/audiotraits/config_fmodAudio.cxx @@ -51,6 +51,8 @@ init_libFmodAudio() { FmodAudioManager::init_type(); FmodAudioSound::init_type(); + AudioManager::register_AudioManager_creator(&Create_FmodAudioManager); + PandaSystem *ps = PandaSystem::get_global_ptr(); ps->add_system("FMOD"); ps->add_system("audio"); diff --git a/panda/src/audiotraits/config_fmodAudio.h b/panda/src/audiotraits/config_fmodAudio.h index 79e221ff31..336f9257d4 100644 --- a/panda/src/audiotraits/config_fmodAudio.h +++ b/panda/src/audiotraits/config_fmodAudio.h @@ -24,7 +24,7 @@ NotifyCategoryDecl(fmodAudio, EXPCL_FMOD_AUDIO, EXPTP_FMOD_AUDIO); extern ConfigVariableInt fmod_audio_preload_threshold; -extern EXPCL_FMOD_AUDIO void init_libFmodAudio(); +extern "C" EXPCL_FMOD_AUDIO void init_libFmodAudio(); extern "C" EXPCL_FMOD_AUDIO Create_AudioManager_proc *get_audio_manager_func_fmod_audio(); #endif // CONFIG_FMODAUDIO_H From ce79656141694e0e8c3477215f8bd5ac7207b4e6 Mon Sep 17 00:00:00 2001 From: deflected Date: Sun, 28 Jan 2018 20:52:43 +0200 Subject: [PATCH 059/118] bullet: Fixed crash when rendering bulletDebugNode - Fixed crash when rendering bulletDebugNode without calls to doPhysics. This is caused by _debug_world pointing to invalid memory, while _debug_stale is true when bulletDebugNode is initialized. Signed-off-by: deflected --- panda/src/bullet/bulletDebugNode.cxx | 4 +++- panda/src/bullet/bulletWorld.cxx | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/panda/src/bullet/bulletDebugNode.cxx b/panda/src/bullet/bulletDebugNode.cxx index b4575dd3cf..9b8c9c0bfb 100644 --- a/panda/src/bullet/bulletDebugNode.cxx +++ b/panda/src/bullet/bulletDebugNode.cxx @@ -31,8 +31,10 @@ PStatCollector BulletDebugNode::_pstat_debug("App:Bullet:DoPhysics:Debug"); * */ BulletDebugNode:: -BulletDebugNode(const char *name) : PandaNode(name), _debug_stale(true) { +BulletDebugNode(const char *name) : PandaNode(name) { + _debug_stale = false; + _debug_world = nullptr; _wireframe = true; _constraints = true; _bounds = false; diff --git a/panda/src/bullet/bulletWorld.cxx b/panda/src/bullet/bulletWorld.cxx index a7942aee02..d60339f360 100644 --- a/panda/src/bullet/bulletWorld.cxx +++ b/panda/src/bullet/bulletWorld.cxx @@ -134,6 +134,7 @@ void BulletWorld:: clear_debug_node() { if (_debug != nullptr) { LightMutexHolder holder(_debug->_lock); + _debug->_debug_stale = false; _debug->_debug_world = nullptr; _world->setDebugDrawer(nullptr); _debug = nullptr; From 50dafd087d18b870188287f9b3a55d3577ba6c41 Mon Sep 17 00:00:00 2001 From: deflected Date: Fri, 2 Feb 2018 11:23:37 +0200 Subject: [PATCH 060/118] bullet: Fixed calculating of Box dimensions - Fixed wrong claculation of box dimensions, when creating BoxShape from solids Signed-off-by: deflected --- panda/src/bullet/bulletBoxShape.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/panda/src/bullet/bulletBoxShape.cxx b/panda/src/bullet/bulletBoxShape.cxx index 02b5af45b3..f523184993 100644 --- a/panda/src/bullet/bulletBoxShape.cxx +++ b/panda/src/bullet/bulletBoxShape.cxx @@ -64,9 +64,9 @@ make_from_solid(const CollisionBox *solid) { LPoint3 p0 = solid->get_min(); LPoint3 p1 = solid->get_max(); - LVecBase3 extents(p1.get_x() - p0.get_x() / 2.0, - p1.get_y() - p0.get_y() / 2.0, - p1.get_z() - p0.get_z() / 2.0); + LVecBase3 extents((p1.get_x() - p0.get_x()) / 2.0, + (p1.get_y() - p0.get_y()) / 2.0, + (p1.get_z() - p0.get_z()) / 2.0); return new BulletBoxShape(extents); } From 0d079e2b8037a98abecb51ef6804ba357129c781 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Sun, 4 Feb 2018 16:27:10 -0700 Subject: [PATCH 061/118] general: Remove execute modes from ordinary text files --- dtool/src/cppparser/cppClosureType.cxx | 0 dtool/src/cppparser/cppClosureType.h | 0 dtool/src/interrogatedb/py_compat.cxx | 0 dtool/src/interrogatedb/py_compat.h | 0 dtool/src/interrogatedb/py_wrappers.cxx | 0 dtool/src/interrogatedb/py_wrappers.h | 0 dtool/src/parser-inc/glew/glew.h | 0 dtool/src/parser-inc/ode/ode.h | 0 dtool/src/parser-inc/ogg/os_types.h | 0 dtool/src/parser-inc/openssl/x509v3.h | 0 panda/src/nativenet/socket_address.cxx | 0 11 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 dtool/src/cppparser/cppClosureType.cxx mode change 100755 => 100644 dtool/src/cppparser/cppClosureType.h mode change 100755 => 100644 dtool/src/interrogatedb/py_compat.cxx mode change 100755 => 100644 dtool/src/interrogatedb/py_compat.h mode change 100755 => 100644 dtool/src/interrogatedb/py_wrappers.cxx mode change 100755 => 100644 dtool/src/interrogatedb/py_wrappers.h mode change 100755 => 100644 dtool/src/parser-inc/glew/glew.h mode change 100755 => 100644 dtool/src/parser-inc/ode/ode.h mode change 100755 => 100644 dtool/src/parser-inc/ogg/os_types.h mode change 100755 => 100644 dtool/src/parser-inc/openssl/x509v3.h mode change 100755 => 100644 panda/src/nativenet/socket_address.cxx diff --git a/dtool/src/cppparser/cppClosureType.cxx b/dtool/src/cppparser/cppClosureType.cxx old mode 100755 new mode 100644 diff --git a/dtool/src/cppparser/cppClosureType.h b/dtool/src/cppparser/cppClosureType.h old mode 100755 new mode 100644 diff --git a/dtool/src/interrogatedb/py_compat.cxx b/dtool/src/interrogatedb/py_compat.cxx old mode 100755 new mode 100644 diff --git a/dtool/src/interrogatedb/py_compat.h b/dtool/src/interrogatedb/py_compat.h old mode 100755 new mode 100644 diff --git a/dtool/src/interrogatedb/py_wrappers.cxx b/dtool/src/interrogatedb/py_wrappers.cxx old mode 100755 new mode 100644 diff --git a/dtool/src/interrogatedb/py_wrappers.h b/dtool/src/interrogatedb/py_wrappers.h old mode 100755 new mode 100644 diff --git a/dtool/src/parser-inc/glew/glew.h b/dtool/src/parser-inc/glew/glew.h old mode 100755 new mode 100644 diff --git a/dtool/src/parser-inc/ode/ode.h b/dtool/src/parser-inc/ode/ode.h old mode 100755 new mode 100644 diff --git a/dtool/src/parser-inc/ogg/os_types.h b/dtool/src/parser-inc/ogg/os_types.h old mode 100755 new mode 100644 diff --git a/dtool/src/parser-inc/openssl/x509v3.h b/dtool/src/parser-inc/openssl/x509v3.h old mode 100755 new mode 100644 diff --git a/panda/src/nativenet/socket_address.cxx b/panda/src/nativenet/socket_address.cxx old mode 100755 new mode 100644 From fbe373dfdc0d78a9b7c83f4ab4fee60b86f66962 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Sun, 4 Feb 2018 15:55:52 -0700 Subject: [PATCH 062/118] general: Fix includes --- dtool/src/interrogatedb/py_compat.cxx | 1 + dtool/src/interrogatedb/py_panda.h | 1 + panda/src/pgraph/shaderAttrib.cxx | 2 ++ panda/src/physics/physicalNode.cxx | 1 + panda/src/pipeline/pipeline.cxx | 1 - panda/src/pipeline/pipeline.h | 2 ++ panda/src/putil/datagramBuffer.h | 1 + 7 files changed, 8 insertions(+), 1 deletion(-) diff --git a/dtool/src/interrogatedb/py_compat.cxx b/dtool/src/interrogatedb/py_compat.cxx index f7f02607c0..f0dd42cf73 100644 --- a/dtool/src/interrogatedb/py_compat.cxx +++ b/dtool/src/interrogatedb/py_compat.cxx @@ -12,6 +12,7 @@ */ #include "py_compat.h" +#include "py_panda.h" #ifdef HAVE_PYTHON diff --git a/dtool/src/interrogatedb/py_panda.h b/dtool/src/interrogatedb/py_panda.h index 74f13680f8..f4edc8f4ad 100644 --- a/dtool/src/interrogatedb/py_panda.h +++ b/dtool/src/interrogatedb/py_panda.h @@ -22,6 +22,7 @@ #include "pnotify.h" #include "vector_uchar.h" +#include "register_type.h" #if defined(HAVE_PYTHON) && !defined(CPPPARSER) diff --git a/panda/src/pgraph/shaderAttrib.cxx b/panda/src/pgraph/shaderAttrib.cxx index 67bba2d196..711e886828 100644 --- a/panda/src/pgraph/shaderAttrib.cxx +++ b/panda/src/pgraph/shaderAttrib.cxx @@ -25,6 +25,8 @@ #include "datagram.h" #include "datagramIterator.h" #include "nodePath.h" +#include "paramNodePath.h" +#include "paramTexture.h" #include "shaderBuffer.h" TypeHandle ShaderAttrib::_type_handle; diff --git a/panda/src/physics/physicalNode.cxx b/panda/src/physics/physicalNode.cxx index 0fdf995720..daa454aff1 100644 --- a/panda/src/physics/physicalNode.cxx +++ b/panda/src/physics/physicalNode.cxx @@ -12,6 +12,7 @@ */ #include "physicalNode.h" +#include "physicsManager.h" // static stuff. TypeHandle PhysicalNode::_type_handle; diff --git a/panda/src/pipeline/pipeline.cxx b/panda/src/pipeline/pipeline.cxx index 8d8cd2bebb..e6fd1b6cee 100644 --- a/panda/src/pipeline/pipeline.cxx +++ b/panda/src/pipeline/pipeline.cxx @@ -13,7 +13,6 @@ #include "pipeline.h" #include "pipelineCyclerTrueImpl.h" -#include "reMutexHolder.h" #include "configVariableInt.h" #include "config_pipeline.h" diff --git a/panda/src/pipeline/pipeline.h b/panda/src/pipeline/pipeline.h index 3311496ff8..1dbba9429f 100644 --- a/panda/src/pipeline/pipeline.h +++ b/panda/src/pipeline/pipeline.h @@ -18,6 +18,8 @@ #include "pipelineCyclerLinks.h" #include "namable.h" #include "pset.h" +#include "pmutex.h" +#include "mutexHolder.h" #include "reMutex.h" #include "reMutexHolder.h" #include "selectThreadImpl.h" // for THREADED_PIPELINE definition diff --git a/panda/src/putil/datagramBuffer.h b/panda/src/putil/datagramBuffer.h index acb67836f9..8962ddbb2b 100644 --- a/panda/src/putil/datagramBuffer.h +++ b/panda/src/putil/datagramBuffer.h @@ -15,6 +15,7 @@ #define DATAGRAMBUFFER_H #include "pandabase.h" +#include "datagramGenerator.h" #include "datagramSink.h" #include "vector_uchar.h" From fb6c92cf08c313ace638daf5254b099e2391c0ef Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Sun, 4 Feb 2018 16:09:30 -0700 Subject: [PATCH 063/118] express: Move all vector_* types to dtoolutil --- dtool/src/dtoolutil/p3dtoolutil_composite2.cxx | 3 +++ .../express => dtool/src/dtoolutil}/vector_double.cxx | 4 ++-- .../express => dtool/src/dtoolutil}/vector_double.h | 10 ++++------ .../express => dtool/src/dtoolutil}/vector_float.cxx | 4 ++-- .../src/express => dtool/src/dtoolutil}/vector_float.h | 10 ++++------ .../express => dtool/src/dtoolutil}/vector_stdfloat.h | 2 +- .../express => dtool/src/dtoolutil}/vector_uchar.cxx | 4 ++-- .../src/express => dtool/src/dtoolutil}/vector_uchar.h | 10 ++++------ panda/src/express/p3express_composite2.cxx | 3 --- 9 files changed, 22 insertions(+), 28 deletions(-) rename {panda/src/express => dtool/src/dtoolutil}/vector_double.cxx (89%) rename {panda/src/express => dtool/src/dtoolutil}/vector_double.h (82%) rename {panda/src/express => dtool/src/dtoolutil}/vector_float.cxx (89%) rename {panda/src/express => dtool/src/dtoolutil}/vector_float.h (81%) rename {panda/src/express => dtool/src/dtoolutil}/vector_stdfloat.h (96%) rename {panda/src/express => dtool/src/dtoolutil}/vector_uchar.cxx (89%) rename {panda/src/express => dtool/src/dtoolutil}/vector_uchar.h (81%) diff --git a/dtool/src/dtoolutil/p3dtoolutil_composite2.cxx b/dtool/src/dtoolutil/p3dtoolutil_composite2.cxx index 2adf532173..10067c84fd 100644 --- a/dtool/src/dtoolutil/p3dtoolutil_composite2.cxx +++ b/dtool/src/dtoolutil/p3dtoolutil_composite2.cxx @@ -8,6 +8,9 @@ #include "stringDecoder.cxx" #include "textEncoder.cxx" #include "unicodeLatinMap.cxx" +#include "vector_double.cxx" +#include "vector_float.cxx" #include "vector_int.cxx" #include "vector_string.cxx" +#include "vector_uchar.cxx" #include "win32ArgParser.cxx" diff --git a/panda/src/express/vector_double.cxx b/dtool/src/dtoolutil/vector_double.cxx similarity index 89% rename from panda/src/express/vector_double.cxx rename to dtool/src/dtoolutil/vector_double.cxx index 165600e2a7..a6def549af 100644 --- a/panda/src/express/vector_double.cxx +++ b/dtool/src/dtoolutil/vector_double.cxx @@ -13,8 +13,8 @@ #include "vector_double.h" -#define EXPCL EXPCL_PANDAEXPRESS -#define EXPTP EXPTP_PANDAEXPRESS +#define EXPCL EXPCL_DTOOL +#define EXPTP EXPTP_DTOOL #define TYPE double #define NAME vector_double diff --git a/panda/src/express/vector_double.h b/dtool/src/dtoolutil/vector_double.h similarity index 82% rename from panda/src/express/vector_double.h rename to dtool/src/dtoolutil/vector_double.h index 1a36c1e425..6757bddc7a 100644 --- a/panda/src/express/vector_double.h +++ b/dtool/src/dtoolutil/vector_double.h @@ -14,19 +14,17 @@ #ifndef VECTOR_DOUBLE_H #define VECTOR_DOUBLE_H -#include "pandabase.h" - -#include "pvector.h" +#include "dtoolbase.h" /** * A vector of doubles. This class is defined once here, and exported to - * PANDA.DLL; other packages that want to use a vector of this type (whether + * DTOOL.DLL; other packages that want to use a vector of this type (whether * they need to export it or not) should include this header file, rather than * defining the vector again. */ -#define EXPCL EXPCL_PANDAEXPRESS -#define EXPTP EXPTP_PANDAEXPRESS +#define EXPCL EXPCL_DTOOL +#define EXPTP EXPTP_DTOOL #define TYPE double #define NAME vector_double diff --git a/panda/src/express/vector_float.cxx b/dtool/src/dtoolutil/vector_float.cxx similarity index 89% rename from panda/src/express/vector_float.cxx rename to dtool/src/dtoolutil/vector_float.cxx index cc0df9289c..a84b030a56 100644 --- a/panda/src/express/vector_float.cxx +++ b/dtool/src/dtoolutil/vector_float.cxx @@ -13,8 +13,8 @@ #include "vector_float.h" -#define EXPCL EXPCL_PANDAEXPRESS -#define EXPTP EXPTP_PANDAEXPRESS +#define EXPCL EXPCL_DTOOL +#define EXPTP EXPTP_DTOOL #define TYPE float #define NAME vector_float diff --git a/panda/src/express/vector_float.h b/dtool/src/dtoolutil/vector_float.h similarity index 81% rename from panda/src/express/vector_float.h rename to dtool/src/dtoolutil/vector_float.h index 862980b15a..36d9f200f9 100644 --- a/panda/src/express/vector_float.h +++ b/dtool/src/dtoolutil/vector_float.h @@ -14,19 +14,17 @@ #ifndef VECTOR_FLOAT_H #define VECTOR_FLOAT_H -#include "pandabase.h" - -#include "pvector.h" +#include "dtoolbase.h" /** * A vector of floats. This class is defined once here, and exported to - * PANDA.DLL; other packages that want to use a vector of this type (whether + * DTOOL.DLL; other packages that want to use a vector of this type (whether * they need to export it or not) should include this header file, rather than * defining the vector again. */ -#define EXPCL EXPCL_PANDAEXPRESS -#define EXPTP EXPTP_PANDAEXPRESS +#define EXPCL EXPCL_DTOOL +#define EXPTP EXPTP_DTOOL #define TYPE float #define NAME vector_float diff --git a/panda/src/express/vector_stdfloat.h b/dtool/src/dtoolutil/vector_stdfloat.h similarity index 96% rename from panda/src/express/vector_stdfloat.h rename to dtool/src/dtoolutil/vector_stdfloat.h index cecf6c18d8..e0100ae6fd 100644 --- a/panda/src/express/vector_stdfloat.h +++ b/dtool/src/dtoolutil/vector_stdfloat.h @@ -14,7 +14,7 @@ #ifndef VECTOR_STDFLOAT_H #define VECTOR_STDFLOAT_H -#include "pandabase.h" +#include "dtoolbase.h" #include "vector_double.h" #include "vector_float.h" diff --git a/panda/src/express/vector_uchar.cxx b/dtool/src/dtoolutil/vector_uchar.cxx similarity index 89% rename from panda/src/express/vector_uchar.cxx rename to dtool/src/dtoolutil/vector_uchar.cxx index 5463ef0724..07434032dd 100644 --- a/panda/src/express/vector_uchar.cxx +++ b/dtool/src/dtoolutil/vector_uchar.cxx @@ -13,8 +13,8 @@ #include "vector_uchar.h" -#define EXPCL EXPCL_PANDAEXPRESS -#define EXPTP EXPTP_PANDAEXPRESS +#define EXPCL EXPCL_DTOOL +#define EXPTP EXPTP_DTOOL #define TYPE unsigned char #define NAME vector_uchar diff --git a/panda/src/express/vector_uchar.h b/dtool/src/dtoolutil/vector_uchar.h similarity index 81% rename from panda/src/express/vector_uchar.h rename to dtool/src/dtoolutil/vector_uchar.h index ce6a1997bb..608472ae7e 100644 --- a/panda/src/express/vector_uchar.h +++ b/dtool/src/dtoolutil/vector_uchar.h @@ -14,19 +14,17 @@ #ifndef VECTOR_UCHAR_H #define VECTOR_UCHAR_H -#include "pandabase.h" - -#include "pvector.h" +#include "dtoolbase.h" /** * A vector of uchars. This class is defined once here, and exported to - * PANDAEXPRESS.DLL; other packages that want to use a vector of this type + * DTOOL.DLL; other packages that want to use a vector of this type * (whether they need to export it or not) should include this header file, * rather than defining the vector again. */ -#define EXPCL EXPCL_PANDAEXPRESS -#define EXPTP EXPTP_PANDAEXPRESS +#define EXPCL EXPCL_DTOOL +#define EXPTP EXPTP_DTOOL #define TYPE unsigned char #define NAME vector_uchar diff --git a/panda/src/express/p3express_composite2.cxx b/panda/src/express/p3express_composite2.cxx index 95c8b0e5fb..5caa03986d 100644 --- a/panda/src/express/p3express_composite2.cxx +++ b/panda/src/express/p3express_composite2.cxx @@ -12,9 +12,6 @@ #include "threadSafePointerToBase.cxx" #include "trueClock.cxx" #include "typedReferenceCount.cxx" -#include "vector_uchar.cxx" -#include "vector_double.cxx" -#include "vector_float.cxx" #include "virtualFile.cxx" #include "virtualFileComposite.cxx" #include "virtualFileList.cxx" From 8a7b47d501d46fbc9e37aa86f3a010ee733187d3 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Sun, 4 Feb 2018 23:06:34 -0700 Subject: [PATCH 064/118] audio: Fix memory leak in OpenAL --- panda/src/audiotraits/openalAudioManager.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/panda/src/audiotraits/openalAudioManager.cxx b/panda/src/audiotraits/openalAudioManager.cxx index 2e82a9c718..8d38eeec49 100644 --- a/panda/src/audiotraits/openalAudioManager.cxx +++ b/panda/src/audiotraits/openalAudioManager.cxx @@ -443,6 +443,7 @@ get_sound_data(MovieAudio *movie, int mode) { alBufferData(sd->_sample, (channels>1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16, data, samples * channels * 2, stream->audio_rate()); + delete[] data; int err = alGetError(); if (err != AL_NO_ERROR) { audio_error("could not fill OpenAL buffer object with data"); From 2dba9357bb626a9b61ea0d7f64c8c853291ee3cc Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 6 Feb 2018 19:22:09 +0100 Subject: [PATCH 065/118] makepanda: update Android cross-compile for clang and NDK r16 Tested on Windows. --- makepanda/makepanda.py | 81 ++++++++++++++------- makepanda/makepandacore.py | 141 ++++++++++++++++++++----------------- 2 files changed, 134 insertions(+), 88 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index b00c947208..2f21483e78 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -821,9 +821,6 @@ if (COMPILER=="GCC"): SmartPkgEnable("FREETYPE", "freetype2", ("freetype"), ("freetype2", "freetype2/freetype/freetype.h")) SmartPkgEnable("HARFBUZZ", "harfbuzz", ("harfbuzz"), ("harfbuzz", "harfbuzz/hb-ft.h")) SmartPkgEnable("GL", "gl", ("GL"), ("GL/gl.h"), framework = "OpenGL") - SmartPkgEnable("GLES", "glesv1_cm", ("GLESv1_CM"), ("GLES/gl.h"), framework = "OpenGLES") - SmartPkgEnable("GLES2", "glesv2", ("GLESv2"), ("GLES2/gl2.h")) #framework = "OpenGLES"? - SmartPkgEnable("EGL", "egl", ("EGL"), ("EGL/egl.h")) SmartPkgEnable("NVIDIACG", "", ("Cg"), "Cg/cg.h", framework = "Cg") SmartPkgEnable("ODE", "", ("ode"), "ode/ode.h", tool = "ode-config") SmartPkgEnable("OPENAL", "openal", ("openal"), "AL/al.h", framework = "OpenAL") @@ -837,6 +834,12 @@ if (COMPILER=="GCC"): SmartPkgEnable("JPEG", "", ("jpeg"), "jpeglib.h") SmartPkgEnable("PNG", "libpng", ("png"), "png.h", tool = "libpng-config") + # On Android, these are system libraries. + if GetHost() != "android": + SmartPkgEnable("GLES", "glesv1_cm", (), ("GLES/gl.h"), framework = "OpenGLES") + SmartPkgEnable("GLES2", "glesv2", (), ("GLES2/gl2.h")) #framework = "OpenGLES"? + SmartPkgEnable("EGL", "egl", (), ("EGL/egl.h")) + if not PkgSkip("FFMPEG"): if GetTarget() == "darwin": LibName("FFMPEG", "-Wl,-read_only_relocs,suppress") @@ -1251,7 +1254,13 @@ def CompileCxx(obj,src,opts): cmd += " -arch %s" % arch if "SYSROOT" in SDK: - cmd += ' --sysroot=%s -no-canonical-prefixes' % (SDK["SYSROOT"]) + if GetTarget() != "android": + cmd += ' --sysroot=%s' % (SDK["SYSROOT"]) + else: + ndk_dir = SDK["ANDROID_NDK"].replace('\\', '/') + cmd += ' -isystem %s/sysroot/usr/include' % (ndk_dir) + cmd += ' -isystem %s/sysroot/usr/include/%s' % (ndk_dir, SDK["ANDROID_TRIPLE"]) + cmd += ' -no-canonical-prefixes' # Android-specific flags. arch = GetTargetArch() @@ -1261,33 +1270,38 @@ def CompileCxx(obj,src,opts): # just copied from the default Android Makefiles. if "ANDROID_API" in SDK: cmd += ' -D__ANDROID_API__=' + str(SDK["ANDROID_API"]) - if "ANDROID_STL" in SDK: - cmd += ' -I%s/include' % (SDK["ANDROID_STL"]) - cmd += ' -I%s/libs/%s/include' % (SDK["ANDROID_STL"], SDK["ANDROID_ABI"]) + if "ANDROID_GCC_TOOLCHAIN" in SDK: + cmd += ' -gcc-toolchain ' + SDK["ANDROID_GCC_TOOLCHAIN"].replace('\\', '/') cmd += ' -ffunction-sections -funwind-tables' if arch == 'armv7a': - cmd += ' -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__' - cmd += ' -fstack-protector -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16' + cmd += ' -target armv7-none-linux-androideabi' + cmd += ' -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16' + cmd += ' -fno-integrated-as' elif arch == 'arm': - cmd += ' -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__' - cmd += ' -fstack-protector -march=armv5te -mtune=xscale -msoft-float' + cmd += ' -target armv5te-none-linux-androideabi' + cmd += ' -march=armv5te -mtune=xscale -msoft-float' + cmd += ' -fno-integrated-as' + elif arch == 'aarch64': + cmd += ' -target aarch64-none-linux-android' elif arch == 'mips': - cmd += ' -finline-functions -fmessage-length=0' - cmd += ' -fno-inline-functions-called-once -fgcse-after-reload' - cmd += ' -frerun-cse-after-loop -frename-registers' + cmd += ' -target mipsel-none-linux-android' + cmd += ' -mips32' + elif arch == 'mips64': + cmd += ' -target mips64el-none-linux-android' + cmd += ' -fintegrated-as' + elif arch == 'x86': + cmd += ' -target i686-none-linux-android' + cmd += ' -mstackrealign' + elif arch == 'x86_64': + cmd += ' -target x86_64-none-linux-android' cmd += " -Wa,--noexecstack" - # Now add specific release/debug flags. - if optlevel >= 3: - cmd += " -fomit-frame-pointer" - if arch.startswith('arm'): - cmd += ' -finline-limit=64 -mthumb' - elif arch == 'mips': - cmd += ' -funswitch-loops -finline-limit=300' - else: - cmd += ' -fno-omit-frame-pointer' - if arch.startswith('arm'): + # Do we want thumb or arm instructions? + if arch.startswith('arm'): + if optlevel >= 3: + cmd += ' -mthumb' + else: cmd += ' -marm' # Enable SIMD instructions if requested @@ -1745,9 +1759,26 @@ def CompileLink(dll, obj, opts): cmd += " -arch %s" % arch elif GetTarget() == 'android': + arch = GetTargetArch() + if "ANDROID_GCC_TOOLCHAIN" in SDK: + cmd += ' -gcc-toolchain ' + SDK["ANDROID_GCC_TOOLCHAIN"].replace('\\', '/') cmd += " -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now" - if GetTargetArch() == 'armv7a': + if arch == 'armv7a': + cmd += ' -target armv7-none-linux-androideabi' cmd += " -march=armv7-a -Wl,--fix-cortex-a8" + elif arch == 'arm': + cmd += ' -target armv5te-none-linux-androideabi' + elif arch == 'aarch64': + cmd += ' -target aarch64-none-linux-android' + elif arch == 'mips': + cmd += ' -target mipsel-none-linux-android' + cmd += ' -mips32' + elif arch == 'mips64': + cmd += ' -target mips64el-none-linux-android' + elif arch == 'x86': + cmd += ' -target i686-none-linux-android' + elif arch == 'x86_64': + cmd += ' -target x86_64-none-linux-android' cmd += ' -lc -lm' else: cmd += " -pthread" diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 75211d3979..bc0cdc7549 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -37,7 +37,8 @@ TARGET_ARCH = None HAS_TARGET_ARCH = False TOOLCHAIN_PREFIX = "" ANDROID_ABI = None -ANDROID_API = 14 +ANDROID_TRIPLE = None +ANDROID_API = None SYS_LIB_DIRS = [] SYS_INC_DIRS = [] DEBUG_DEPENDENCIES = False @@ -359,40 +360,48 @@ def SetTarget(target, arch=None): if host == 'android': arch = host_arch else: - arch = 'arm' + arch = 'armv7a' # Did we specify an API level? + global ANDROID_API target, _, api = target.partition('-') if api: - global ANDROID_API ANDROID_API = int(api) + elif arch in ('mips64', 'aarch64', 'x86_64'): + # 64-bit platforms were introduced in Android 21. + ANDROID_API = 21 + else: + # Default to the lowest API level supported by NDK r16. + ANDROID_API = 14 # Determine the prefix for our gcc tools, eg. arm-linux-androideabi-gcc - global ANDROID_ABI + global ANDROID_ABI, ANDROID_TRIPLE if arch == 'armv7a': ANDROID_ABI = 'armeabi-v7a' - TOOLCHAIN_PREFIX = 'arm-linux-androideabi-' + ANDROID_TRIPLE = 'arm-linux-androideabi' elif arch == 'arm': ANDROID_ABI = 'armeabi' - TOOLCHAIN_PREFIX = 'arm-linux-androideabi-' + ANDROID_TRIPLE = 'arm-linux-androideabi' elif arch == 'aarch64': ANDROID_ABI = 'arm64-v8a' - TOOLCHAIN_PREFIX = 'aarch64-linux-android-' + ANDROID_TRIPLE = 'aarch64-linux-android' elif arch == 'mips': ANDROID_ABI = 'mips' - TOOLCHAIN_PREFIX = 'mipsel-linux-android-' + ANDROID_TRIPLE = 'mipsel-linux-android' elif arch == 'mips64': ANDROID_ABI = 'mips64' - TOOLCHAIN_PREFIX = 'mips64el-linux-android-' + ANDROID_TRIPLE = 'mips64el-linux-android' elif arch == 'x86': ANDROID_ABI = 'x86' - TOOLCHAIN_PREFIX = 'i686-linux-android-' + ANDROID_TRIPLE = 'i686-linux-android' elif arch == 'x86_64': ANDROID_ABI = 'x86_64' - TOOLCHAIN_PREFIX = 'x86_64-linux-android-' + ANDROID_TRIPLE = 'x86_64-linux-android' else: exit('Android architecture must be arm, armv7a, aarch64, mips, mips64, x86 or x86_64') + TOOLCHAIN_PREFIX = ANDROID_TRIPLE + '-' + elif target == 'linux': if arch is not None: TOOLCHAIN_PREFIX = '%s-linux-gnu-' % arch @@ -2359,6 +2368,8 @@ def SdkLocateAndroid(): """This actually locates the Android NDK, not the Android SDK. NDK_ROOT must be set to its root directory.""" + global TOOLCHAIN_PREFIX + if GetTarget() != 'android': return @@ -2368,6 +2379,7 @@ def SdkLocateAndroid(): abi = ANDROID_ABI SDK["ANDROID_ABI"] = abi + SDK["ANDROID_TRIPLE"] = ANDROID_TRIPLE if GetHost() == 'android': return @@ -2383,36 +2395,58 @@ def SdkLocateAndroid(): SDK["ANDROID_NDK"] = ndk_root # Determine the toolchain location. - gcc_ver = '4.8' + prebuilt_dir = os.path.join(ndk_root, 'toolchains', 'llvm', 'prebuilt') + if not os.path.isdir(prebuilt_dir): + exit('Not found: %s' % (prebuilt_dir)) + + host_tag = GetHost() + '-x86' + if host_64: + host_tag += '_64' + elif host_tag == 'windows-x86': + host_tag = 'windows' + + prebuilt_dir = os.path.join(prebuilt_dir, host_tag) + if host_tag == 'windows-x86_64' and not os.path.isdir(prebuilt_dir): + # Try the 32-bits toolchain instead. + host_tag = 'windows' + prebuilt_dir = os.path.join(prebuilt_dir, host_tag) + + SDK["ANDROID_TOOLCHAIN"] = prebuilt_dir + + # And locate the GCC toolchain, which is needed for some tools (eg. as/ld) arch = GetTargetArch() - if arch == 'armv7a' or arch == 'arm': - arch = 'arm' - toolchain = 'arm-linux-androideabi-' + gcc_ver - elif arch == 'aarch64': - toolchain = 'aarch64-linux-android-' + gcc_ver - elif arch == 'mips': - toolchain = 'mipsel-linux-android-' + gcc_ver - elif arch == 'mips64': - toolchain = 'mips64el-linux-android-' + gcc_ver - elif arch == 'x86': - toolchain = 'x86-' + gcc_ver - elif arch == 'x86_64': - toolchain = 'x86_64-' + gcc_ver - SDK["ANDROID_TOOLCHAIN"] = os.path.join(ndk_root, 'toolchains', toolchain) + for opt in (TOOLCHAIN_PREFIX + '4.9', arch + '-4.9', TOOLCHAIN_PREFIX + '4.8', arch + '-4.8'): + if os.path.isdir(os.path.join(ndk_root, 'toolchains', opt)): + SDK["ANDROID_GCC_TOOLCHAIN"] = os.path.join(ndk_root, 'toolchains', opt, 'prebuilt', host_tag) + break + + # The prebuilt binaries have no toolchain prefix. + TOOLCHAIN_PREFIX = '' # Determine the sysroot directory. - SDK["SYSROOT"] = os.path.join(ndk_root, 'platforms', 'android-%s' % (api), 'arch-%s' % (arch)) + if arch == 'armv7a': + arch_dir = 'arch-arm' + elif arch == 'aarch64': + arch_dir = 'arch-arm64' + else: + arch_dir = 'arch-' + arch + SDK["SYSROOT"] = os.path.join(ndk_root, 'platforms', 'android-%s' % (api), arch_dir).replace('\\', '/') #IncDirectory("ALWAYS", os.path.join(SDK["SYSROOT"], 'usr', 'include')) - stdlibc = os.path.join(ndk_root, 'sources', 'cxx-stl', 'gnu-libstdc++', gcc_ver) - SDK["ANDROID_STL"] = stdlibc + # Starting with NDK r16, libc++ is the recommended STL to use. + stdlibc = os.path.join(ndk_root, 'sources', 'cxx-stl', 'llvm-libc++') + IncDirectory("ALWAYS", os.path.join(stdlibc, 'include').replace('\\', '/')) + LibDirectory("ALWAYS", os.path.join(stdlibc, 'libs', abi).replace('\\', '/')) - #IncDirectory("ALWAYS", os.path.join(stdlibc, 'include')) - #IncDirectory("ALWAYS", os.path.join(stdlibc, 'libs', abi, 'include')) + stl_lib = os.path.join(stdlibc, 'libs', abi, 'libc++_shared.so') + LibName("ALWAYS", stl_lib.replace('\\', '/')) + CopyFile(os.path.join(GetOutputDir(), 'lib', 'libc++_shared.so'), stl_lib) - stl_lib = os.path.join(stdlibc, 'libs', abi, 'libgnustl_shared.so') - LibName("ALWAYS", stl_lib) - CopyFile(os.path.join(GetOutputDir(), 'libs', abi, 'libgnustl_shared.so'), stl_lib) + # The Android support library polyfills C++ features not available in the + # STL that ships with Android. + support = os.path.join(ndk_root, 'sources', 'android', 'support', 'include') + IncDirectory("ALWAYS", support.replace('\\', '/')) + LibName("ALWAYS", "-landroid_support") ######################################################################## ## @@ -2675,6 +2709,10 @@ def SetupBuildEnvironment(compiler): os.environ["LC_ALL"] = "en_US.UTF-8" os.environ["LANGUAGE"] = "en" + # In the case of Android, we have to put the toolchain on the PATH in order to use it. + if GetTarget() == 'android' and GetHost() != 'android': + AddToPathEnv("PATH", os.path.join(SDK["ANDROID_TOOLCHAIN"], "bin")) + if compiler == "MSVC": # Add the visual studio tools to PATH et al. SetupVisualStudioEnviron() @@ -2709,11 +2747,15 @@ def SetupBuildEnvironment(compiler): continue line = line[12:].strip() - for libdir in line.split(':'): - libdir = os.path.normpath(libdir) + libdirs = line.split(':') + while libdirs: + libdir = os.path.normpath(libdirs.pop(0)) if os.path.isdir(libdir): if libdir not in SYS_LIB_DIRS: SYS_LIB_DIRS.append(libdir) + elif len(libdir) == 1: + # Oops, is this a drive letter? Prepend it to the next. + libdirs[0] = libdir + ':' + libdirs[0] elif GetVerbose(): print("Ignoring non-existent library directory %s" % (libdir)) @@ -2771,33 +2813,6 @@ def SetupBuildEnvironment(compiler): for dir in SYS_INC_DIRS: print(" " + dir) - # In the case of Android, we have to put the toolchain on the PATH in order to use it. - if GetTarget() == 'android' and GetHost() != 'android': - # Locate the directory where the toolchain binaries reside. - prebuilt_dir = os.path.join(SDK['ANDROID_TOOLCHAIN'], 'prebuilt') - if not os.path.isdir(prebuilt_dir): - exit('Not found: %s' % (prebuilt_dir)) - - host_tag = GetHost() + '-x86' - if host_64: - host_tag += '_64' - elif host_tag == 'windows-x86': - host_tag = 'windows' - - prebuilt_dir = os.path.join(prebuilt_dir, host_tag) - if host_64 and not os.path.isdir(prebuilt_dir): - # Try the 32-bits toolchain instead. - prebuilt_dir = os.path.join(prebuilt_dir, host_tag) - - if not os.path.isdir(prebuilt_dir): - if host_64: - exit('Not found: %s or %s' % (prebuilt_dir, host_tag)) - else: - exit('Not found: %s' % (prebuilt_dir)) - - # Then, add it to the PATH. - AddToPathEnv("PATH", os.path.join(prebuilt_dir, 'bin')) - # If we're cross-compiling, no point in putting our output dirs on the path. if CrossCompiling(): return From c1fccd311bf0978b8a5edc8c99ebee124b60252c Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 6 Feb 2018 22:31:02 +0100 Subject: [PATCH 066/118] android: properly support multiple Java threads --- panda/src/android/PandaActivity.java | 4 + panda/src/android/android_main.cxx | 34 ++++++-- panda/src/android/config_android.cxx | 9 ++- .../src/android/pnmFileTypeAndroidReader.cxx | 9 ++- panda/src/express/config_express.cxx | 37 --------- panda/src/express/config_express.h | 9 --- panda/src/pipeline/thread.I | 11 +++ panda/src/pipeline/thread.h | 8 ++ panda/src/pipeline/threadPosixImpl.I | 16 ++++ panda/src/pipeline/threadPosixImpl.cxx | 77 ++++++++++++++++--- panda/src/pipeline/threadPosixImpl.h | 14 ++++ 11 files changed, 163 insertions(+), 65 deletions(-) diff --git a/panda/src/android/PandaActivity.java b/panda/src/android/PandaActivity.java index feba4baa4e..8f3240900e 100644 --- a/panda/src/android/PandaActivity.java +++ b/panda/src/android/PandaActivity.java @@ -41,6 +41,10 @@ public class PandaActivity extends NativeActivity { return BitmapFactory.decodeStream(stream, null, options); } + protected static String getCurrentThreadName() { + return Thread.currentThread().getName(); + } + static { System.loadLibrary("gnustl_shared"); System.loadLibrary("p3dtool"); diff --git a/panda/src/android/android_main.cxx b/panda/src/android/android_main.cxx index 32c692489f..8a3d6135fd 100644 --- a/panda/src/android/android_main.cxx +++ b/panda/src/android/android_main.cxx @@ -16,6 +16,7 @@ #include "virtualFileMountAndroidAsset.h" #include "virtualFileSystem.h" #include "filename.h" +#include "thread.h" #include "config_display.h" // #define OPENGLES_1 #include "config_androiddisplay.h" @@ -29,21 +30,44 @@ extern int main(int argc, char **argv); /** * This function is called by native_app_glue to initialize the program. It * simply stores the android_app object and calls main() normally. + * + * Note that this does not run in the main thread, but in a thread created + * specifically for this activity by android_native_app_glue. */ void android_main(struct android_app* app) { panda_android_app = app; - // Attach the current thread to the JVM. + // Attach the app thread to the Java VM. JNIEnv *env; ANativeActivity* activity = app->activity; - int status = activity->vm->AttachCurrentThread(&env, NULL); - if (status < 0 || env == NULL) { + int status = activity->vm->AttachCurrentThread(&env, nullptr); + if (status < 0 || env == nullptr) { android_cat.error() << "Failed to attach thread to JVM!\n"; return; } - // Fetch the data directory. jclass activity_class = env->GetObjectClass(activity->clazz); + + // Get the current Java thread name. This just helps with debugging. + jmethodID methodID = env->GetStaticMethodID(activity_class, "getCurrentThreadName", "()Ljava/lang/String;"); + jstring jthread_name = (jstring) env->CallStaticObjectMethod(activity_class, methodID); + + string thread_name; + if (jthread_name != nullptr) { + const char *c_str = env->GetStringUTFChars(jthread_name, nullptr); + thread_name.assign(c_str); + env->ReleaseStringUTFChars(jthread_name, c_str); + } + + // Before we make any Panda calls, we must make the thread known to Panda. + // This will also cause the JNIEnv pointer to be stored on the thread. + // Note that we must keep a reference to this thread around. + PT(Thread) current_thread = Thread::bind_thread(thread_name, "android_app"); + + android_cat.info() + << "New native activity started on " << *current_thread << "\n"; + + // Fetch the data directory. jmethodID get_appinfo = env->GetMethodID(activity_class, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;"); jobject appinfo = env->CallObjectMethod(activity->clazz, get_appinfo); @@ -77,7 +101,7 @@ void android_main(struct android_app* app) { } // Get the path to the APK. - jmethodID methodID = env->GetMethodID(activity_class, "getPackageCodePath", "()Ljava/lang/String;"); + methodID = env->GetMethodID(activity_class, "getPackageCodePath", "()Ljava/lang/String;"); jstring code_path = (jstring) env->CallObjectMethod(activity->clazz, methodID); const char* apk_path; diff --git a/panda/src/android/config_android.cxx b/panda/src/android/config_android.cxx index bf9056c562..92d986669d 100644 --- a/panda/src/android/config_android.cxx +++ b/panda/src/android/config_android.cxx @@ -49,8 +49,9 @@ init_libandroid() { jint JNI_OnLoad(JavaVM *jvm, void *reserved) { init_libandroid(); - JNIEnv *env = get_jni_env(); - assert(env != NULL); + Thread *thread = Thread::get_current_thread(); + JNIEnv *env = thread->get_jni_env(); + nassertr(env != nullptr, -1); jni_PandaActivity = env->FindClass("org/panda3d/android/PandaActivity"); jni_PandaActivity = (jclass) env->NewGlobalRef(jni_PandaActivity); @@ -75,7 +76,9 @@ jint JNI_OnLoad(JavaVM *jvm, void *reserved) { * references. */ void JNI_OnUnload(JavaVM *jvm, void *reserved) { - JNIEnv *env = get_jni_env(); + Thread *thread = Thread::get_current_thread(); + JNIEnv *env = thread->get_jni_env(); + nassertv(env != nullptr); env->DeleteGlobalRef(jni_PandaActivity); env->DeleteGlobalRef(jni_BitmapFactory_Options); diff --git a/panda/src/android/pnmFileTypeAndroidReader.cxx b/panda/src/android/pnmFileTypeAndroidReader.cxx index 50c69cb59c..ac95ce98fd 100644 --- a/panda/src/android/pnmFileTypeAndroidReader.cxx +++ b/panda/src/android/pnmFileTypeAndroidReader.cxx @@ -76,7 +76,14 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) : } streampos pos = _file->tellg(); - _env = get_jni_env(); + + Thread *current_thread = Thread::get_current_thread(); + _env = current_thread->get_jni_env(); + nassertd(_env != nullptr) { + _is_valid = false; + return; + } + jobject opts = _env->CallStaticObjectMethod(jni_PandaActivity, jni_PandaActivity_readBitmapSize, (jlong) _file); diff --git a/panda/src/express/config_express.cxx b/panda/src/express/config_express.cxx index dd590fcb38..042b6d8db1 100644 --- a/panda/src/express/config_express.cxx +++ b/panda/src/express/config_express.cxx @@ -194,40 +194,3 @@ get_config_express() { static DConfig config_express; return config_express; } - -#ifdef ANDROID -static JavaVM *panda_jvm = NULL; - -/** - * Called by Java when loading this library. - */ -jint JNI_OnLoad(JavaVM *jvm, void *reserved) { - panda_jvm = jvm; - return JNI_VERSION_1_4; -} - -/** - * Returns a pointer to the JavaVM object. - */ -JavaVM *get_java_vm() { - nassertr(panda_jvm != NULL, NULL); - return panda_jvm; -} - -/** - * Returns a JNIEnv object for the current thread. If it doesn't already - * exist, attaches the JVM to this thread. - */ -JNIEnv *get_jni_env() { - nassertr(panda_jvm != NULL, NULL); - JNIEnv *env = NULL; - int status = panda_jvm->GetEnv((void**) &env, JNI_VERSION_1_4); - - if (status < 0 || env == NULL) { - express_cat.error() << "JVM is not available in this thread!\n"; - return NULL; - } - - return env; -} -#endif diff --git a/panda/src/express/config_express.h b/panda/src/express/config_express.h index 53f33ca36a..eabe20f834 100644 --- a/panda/src/express/config_express.h +++ b/panda/src/express/config_express.h @@ -28,10 +28,6 @@ #include "executionEnvironment.h" #include "lineStream.h" -#ifdef ANDROID -#include -#endif - ConfigureDecl(config_express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS); NotifyCategoryDecl(express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS); NotifyCategoryDecl(clock, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS); @@ -65,9 +61,4 @@ END_PUBLISH extern EXPCL_PANDAEXPRESS void init_libexpress(); -#ifdef ANDROID -extern EXPCL_PANDAEXPRESS JavaVM *get_java_vm(); -extern EXPCL_PANDAEXPRESS JNIEnv *get_jni_env(); -#endif - #endif /* __CONFIG_UTIL_H__ */ diff --git a/panda/src/pipeline/thread.I b/panda/src/pipeline/thread.I index 4fcd8a447e..10d7b6e44f 100644 --- a/panda/src/pipeline/thread.I +++ b/panda/src/pipeline/thread.I @@ -300,6 +300,17 @@ prepare_for_exit() { ThreadImpl::prepare_for_exit(); } +#ifdef ANDROID +/** + * Enables interaction with the Java VM on Android. Returns null if the + * thread is not attached to the Java VM (or bind_thread was not called). + */ +INLINE JNIEnv *Thread:: +get_jni_env() const { + return _impl.get_jni_env(); +} +#endif + /** * Stores a PStats index to be associated with this thread. This is used * internally by the PStatClient; you should not need to call this directly. diff --git a/panda/src/pipeline/thread.h b/panda/src/pipeline/thread.h index b519aa75a6..730deb5872 100644 --- a/panda/src/pipeline/thread.h +++ b/panda/src/pipeline/thread.h @@ -23,6 +23,10 @@ #include "pnotify.h" #include "config_pipeline.h" +#ifdef ANDROID +typedef struct _JNIEnv JNIEnv; +#endif + class Mutex; class ReMutex; class MutexDebug; @@ -128,6 +132,10 @@ public: INLINE void set_pstats_callback(PStatsCallback *pstats_callback); INLINE PStatsCallback *get_pstats_callback() const; +#ifdef ANDROID + INLINE JNIEnv *get_jni_env() const; +#endif + private: static void init_main_thread(); static void init_external_thread(); diff --git a/panda/src/pipeline/threadPosixImpl.I b/panda/src/pipeline/threadPosixImpl.I index 93bdce8e8c..62b6026061 100644 --- a/panda/src/pipeline/threadPosixImpl.I +++ b/panda/src/pipeline/threadPosixImpl.I @@ -21,6 +21,9 @@ ThreadPosixImpl(Thread *parent_obj) : _joinable = false; _detached = false; _status = S_new; +#ifdef ANDROID + _jni_env = nullptr; +#endif } /** @@ -60,6 +63,9 @@ bind_thread(Thread *thread) { } int result = pthread_setspecific(_pt_ptr_index, thread); nassertv(result == 0); +#ifdef ANDROID + bind_java_thread(); +#endif } /** @@ -112,3 +118,13 @@ yield() { INLINE void ThreadPosixImpl:: consider_yield() { } + +#ifdef ANDROID +/** + * Returns the JNIEnv object for the current thread. + */ +INLINE JNIEnv *ThreadPosixImpl:: +get_jni_env() const { + return _jni_env; +} +#endif diff --git a/panda/src/pipeline/threadPosixImpl.cxx b/panda/src/pipeline/threadPosixImpl.cxx index 54b3dfd630..5104c67926 100644 --- a/panda/src/pipeline/threadPosixImpl.cxx +++ b/panda/src/pipeline/threadPosixImpl.cxx @@ -24,6 +24,8 @@ #ifdef ANDROID #include "config_express.h" #include + +static JavaVM *java_vm = nullptr; #endif pthread_key_t ThreadPosixImpl::_pt_ptr_index = 0; @@ -183,6 +185,53 @@ get_unique_id() const { return strm.str(); } +#ifdef ANDROID +/** + * Attaches the thread to the Java virtual machine. If this returns true, a + * JNIEnv pointer can be acquired using get_jni_env(). + */ +bool ThreadPosixImpl:: +attach_java_vm() { + JNIEnv *env; + string thread_name = _parent_obj->get_name(); + JavaVMAttachArgs args; + args.version = JNI_VERSION_1_2; + args.name = thread_name.c_str(); + args.group = nullptr; + if (java_vm->AttachCurrentThread(&env, &args) != 0) { + thread_cat.error() + << "Failed to attach Java VM to thread " + << _parent_obj->get_name() << "!\n"; + _jni_env = nullptr; + return false; + } + _jni_env = env; + return true; +} + +/** + * Binds the Panda thread to the current thread, assuming that the current + * thread is already a valid attached Java thread. Called by JNI_OnLoad. + */ +void ThreadPosixImpl:: +bind_java_thread() { + Thread *thread = Thread::get_current_thread(); + nassertv(thread != nullptr); + + // Get the JNIEnv for this Java thread, and store it on the corresponding + // Panda thread object. + JNIEnv *env; + if (java_vm->GetEnv((void **)&env, JNI_VERSION_1_4) == JNI_OK) { + nassertv(thread->_impl._jni_env == nullptr || thread->_impl._jni_env == env); + thread->_impl._jni_env = env; + } else { + thread_cat->error() + << "Called bind_java_thread() on thread " + << *thread << ", which is not attached to Java VM!\n"; + } +} +#endif // ANDROID + /** * The entry point of each thread. */ @@ -209,14 +258,7 @@ root_func(void *data) { #ifdef ANDROID // Attach the Java VM to allow calling Java functions in this thread. - JavaVM *jvm = get_java_vm(); - JNIEnv *env; - if (jvm == NULL || jvm->AttachCurrentThread(&env, NULL) != 0) { - thread_cat.error() - << "Failed to attach Java VM to thread " - << self->_parent_obj->get_name() << "!\n"; - env = NULL; - } + self->attach_java_vm(); #endif self->_parent_obj->thread_main(); @@ -238,8 +280,10 @@ root_func(void *data) { } #ifdef ANDROID - if (env != NULL) { - jvm->DetachCurrentThread(); + // We cannot let the thread end without detaching it. + if (self->_jni_env != nullptr) { + java_vm->DetachCurrentThread(); + self->_jni_env = nullptr; } #endif @@ -276,4 +320,17 @@ init_pt_ptr_index() { nassertv(result == 0); } +#ifdef ANDROID +/** + * Called by Java when loading this library from the Java virtual machine. + */ +jint JNI_OnLoad(JavaVM *jvm, void *reserved) { + // Store the JVM pointer globally. + java_vm = jvm; + + ThreadPosixImpl::bind_java_thread(); + return JNI_VERSION_1_4; +} +#endif // ANDROID + #endif // THREAD_POSIX_IMPL diff --git a/panda/src/pipeline/threadPosixImpl.h b/panda/src/pipeline/threadPosixImpl.h index 0168c38665..ee279c98dd 100644 --- a/panda/src/pipeline/threadPosixImpl.h +++ b/panda/src/pipeline/threadPosixImpl.h @@ -25,6 +25,10 @@ #include +#ifdef ANDROID +typedef struct _JNIEnv JNIEnv; +#endif + class Thread; /** @@ -53,6 +57,12 @@ public: INLINE static void yield(); INLINE static void consider_yield(); +#ifdef ANDROID + INLINE JNIEnv *get_jni_env() const; + bool attach_java_vm(); + static void bind_java_thread(); +#endif + private: static void *root_func(void *data); static void init_pt_ptr_index(); @@ -72,6 +82,10 @@ private: bool _detached; PStatus _status; +#ifdef ANDROID + JNIEnv *_jni_env; +#endif + static pthread_key_t _pt_ptr_index; static bool _got_pt_ptr_index; }; From 10789f6936dc7fd8ac67e4e69d45208ff73c0d70 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 6 Feb 2018 22:33:34 +0100 Subject: [PATCH 067/118] android: fix issue loading some image files --- panda/src/android/pnmFileTypeAndroid.cxx | 5 ++++- panda/src/android/pnmFileTypeAndroidReader.cxx | 1 + panda/src/pnmimagetypes/pnmFileTypeStbImage.cxx | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/panda/src/android/pnmFileTypeAndroid.cxx b/panda/src/android/pnmFileTypeAndroid.cxx index fae5d147c4..c661c6b3b0 100644 --- a/panda/src/android/pnmFileTypeAndroid.cxx +++ b/panda/src/android/pnmFileTypeAndroid.cxx @@ -21,7 +21,10 @@ #include "bamReader.h" static const char * const extensions_android[] = { - "jpg", "jpeg", "gif", "png",//"webp" (android 4.0+) + "jpg", "jpeg", "gif", "png", +#if __ANDROID_API__ >= 14 + "webp" +#endif }; static const int num_extensions_android = sizeof(extensions_android) / sizeof(const char *); diff --git a/panda/src/android/pnmFileTypeAndroidReader.cxx b/panda/src/android/pnmFileTypeAndroidReader.cxx index ac95ce98fd..e18ebcbaea 100644 --- a/panda/src/android/pnmFileTypeAndroidReader.cxx +++ b/panda/src/android/pnmFileTypeAndroidReader.cxx @@ -87,6 +87,7 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) : jobject opts = _env->CallStaticObjectMethod(jni_PandaActivity, jni_PandaActivity_readBitmapSize, (jlong) _file); + _file->clear(); _file->seekg(pos); if (_file->tellg() != pos) { android_cat.error() diff --git a/panda/src/pnmimagetypes/pnmFileTypeStbImage.cxx b/panda/src/pnmimagetypes/pnmFileTypeStbImage.cxx index a87aae11f6..e17e0e9df8 100644 --- a/panda/src/pnmimagetypes/pnmFileTypeStbImage.cxx +++ b/panda/src/pnmimagetypes/pnmFileTypeStbImage.cxx @@ -62,7 +62,7 @@ static const char *const stb_extensions[] = { // Expose the extensions that we don't already expose through other loaders. -#ifndef HAVE_JPEG +#if !defined(HAVE_JPEG) && !defined(ANDROID) "jpg", "jpeg", #endif #ifndef HAVE_PNG From ae0f82911b46dae28498b42168ca99ab215bf798 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 6 Feb 2018 22:36:21 +0100 Subject: [PATCH 068/118] android: allow launching pview by opening models (using Intents) Also add code to show notification toasts. Also enable model cache by default (pointing to app cache dir) --- panda/src/android/PandaActivity.java | 44 +++++++++++++++++++++++----- panda/src/android/android_main.cxx | 44 ++++++++++++++++++++++++++-- panda/src/android/config_android.cxx | 18 ++++++++++++ panda/src/android/config_android.h | 4 +++ panda/src/android/pview.cxx | 16 ++++++---- panda/src/android/pview_manifest.xml | 40 +++++++++++++++++++++++-- 6 files changed, 147 insertions(+), 19 deletions(-) diff --git a/panda/src/android/PandaActivity.java b/panda/src/android/PandaActivity.java index 8f3240900e..91e6c38533 100644 --- a/panda/src/android/PandaActivity.java +++ b/panda/src/android/PandaActivity.java @@ -14,6 +14,9 @@ package org.panda3d.android; import android.app.NativeActivity; +import android.content.Intent; +import android.net.Uri; +import android.widget.Toast; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import org.panda3d.android.NativeIStream; @@ -45,14 +48,41 @@ public class PandaActivity extends NativeActivity { return Thread.currentThread().getName(); } + public String getIntentDataPath() { + Intent intent = getIntent(); + Uri data = intent.getData(); + if (data == null) { + return null; + } + String path = data.getPath(); + if (path.startsWith("//")) { + path = path.substring(1); + } + return path; + } + + public String getCacheDirString() { + return getCacheDir().toString(); + } + + public void showToast(final String text, final int duration) { + final PandaActivity activity = this; + runOnUiThread(new Runnable() { + public void run() { + Toast toast = Toast.makeText(activity, text, duration); + toast.show(); + } + }); + } + static { - System.loadLibrary("gnustl_shared"); - System.loadLibrary("p3dtool"); - System.loadLibrary("p3dtoolconfig"); - System.loadLibrary("pandaexpress"); - System.loadLibrary("panda"); - System.loadLibrary("p3android"); - System.loadLibrary("p3framework"); + //System.loadLibrary("gnustl_shared"); + //System.loadLibrary("p3dtool"); + //System.loadLibrary("p3dtoolconfig"); + //System.loadLibrary("pandaexpress"); + //System.loadLibrary("panda"); + //System.loadLibrary("p3android"); + //System.loadLibrary("p3framework"); System.loadLibrary("pandaegg"); System.loadLibrary("pandagles"); } diff --git a/panda/src/android/android_main.cxx b/panda/src/android/android_main.cxx index 8a3d6135fd..c19ba7d58b 100644 --- a/panda/src/android/android_main.cxx +++ b/panda/src/android/android_main.cxx @@ -25,7 +25,7 @@ // struct android_app* panda_android_app = NULL; -extern int main(int argc, char **argv); +extern int main(int argc, const char **argv); /** * This function is called by native_app_glue to initialize the program. It @@ -100,6 +100,20 @@ void android_main(struct android_app* app) { } } + // Get the cache directory. Set the model-path to this location. + methodID = env->GetMethodID(activity_class, "getCacheDirString", "()Ljava/lang/String;"); + jstring jcache_dir = (jstring) env->CallObjectMethod(activity->clazz, methodID); + + if (jcache_dir != nullptr) { + const char *cache_dir; + cache_dir = env->GetStringUTFChars(jcache_dir, nullptr); + android_cat.info() << "Path to cache: " << cache_dir << "\n"; + + ConfigVariableFilename model_cache_dir("model-cache-dir", Filename()); + model_cache_dir.set_value(cache_dir); + env->ReleaseStringUTFChars(jcache_dir, cache_dir); + } + // Get the path to the APK. methodID = env->GetMethodID(activity_class, "getPackageCodePath", "()Ljava/lang/String;"); jstring code_path = (jstring) env->CallObjectMethod(activity->clazz, methodID); @@ -129,9 +143,29 @@ void android_main(struct android_app* app) { //TODO: prevent it from adding the directory multiple times. get_model_path().append_directory(asset_dir); + // Also read the intent filename. + methodID = env->GetMethodID(activity_class, "getIntentDataPath", "()Ljava/lang/String;"); + jstring filename = (jstring) env->CallObjectMethod(activity->clazz, methodID); + const char *filename_str = nullptr; + if (filename != nullptr) { + filename_str = env->GetStringUTFChars(filename, nullptr); + android_cat.info() << "Got intent filename: " << filename_str << "\n"; + + Filename fn(filename_str); + if (!fn.exists()) { + // Show a toast with the failure message. + android_show_toast(activity, string("Unable to access ") + filename_str, 1); + } + } + // Create bogus argc and argv for calling the main function. - char *argv[] = {nullptr}; - int argc = 0; + const char *argv[] = {"pview", nullptr, nullptr}; + int argc = 1; + + if (filename_str != nullptr) { + argv[1] = filename_str; + ++argc; + } while (!app->destroyRequested) { // Call the main function. This will not return until the app is done. @@ -173,6 +207,10 @@ void android_main(struct android_app* app) { android_cat.info() << "Destroy requested, exiting from android_main\n"; + if (filename_str != nullptr) { + env->ReleaseStringUTFChars(filename, filename_str); + } + // Detach the thread before exiting. activity->vm->DetachCurrentThread(); } diff --git a/panda/src/android/config_android.cxx b/panda/src/android/config_android.cxx index 92d986669d..019443be4b 100644 --- a/panda/src/android/config_android.cxx +++ b/panda/src/android/config_android.cxx @@ -24,6 +24,7 @@ struct android_app *panda_android_app = NULL; jclass jni_PandaActivity; jmethodID jni_PandaActivity_readBitmapSize; jmethodID jni_PandaActivity_readBitmap; +jmethodID jni_PandaActivity_showToast; jclass jni_BitmapFactory_Options; jfieldID jni_BitmapFactory_Options_outWidth; @@ -62,6 +63,9 @@ jint JNI_OnLoad(JavaVM *jvm, void *reserved) { jni_PandaActivity_readBitmap = env->GetStaticMethodID(jni_PandaActivity, "readBitmap", "(JI)Landroid/graphics/Bitmap;"); + jni_PandaActivity_showToast = env->GetMethodID(jni_PandaActivity, + "showToast", "(Ljava/lang/String;I)V"); + jni_BitmapFactory_Options = env->FindClass("android/graphics/BitmapFactory$Options"); jni_BitmapFactory_Options = (jclass) env->NewGlobalRef(jni_BitmapFactory_Options); @@ -83,3 +87,17 @@ void JNI_OnUnload(JavaVM *jvm, void *reserved) { env->DeleteGlobalRef(jni_PandaActivity); env->DeleteGlobalRef(jni_BitmapFactory_Options); } + +/** + * Shows a toast notification at the bottom of the activity. The duration + * should be 0 for short and 1 for long. + */ +void android_show_toast(ANativeActivity *activity, const string &message, int duration) { + Thread *thread = Thread::get_current_thread(); + JNIEnv *env = thread->get_jni_env(); + nassertv(env != nullptr); + + jstring jmsg = env->NewStringUTF(message.c_str()); + env->CallVoidMethod(activity->clazz, jni_PandaActivity_showToast, jmsg, (jint)duration); + env->DeleteLocalRef(jmsg); +} diff --git a/panda/src/android/config_android.h b/panda/src/android/config_android.h index a67ac2423f..74599ec3d9 100644 --- a/panda/src/android/config_android.h +++ b/panda/src/android/config_android.h @@ -20,6 +20,7 @@ #include "configVariableBool.h" #include "configVariableInt.h" +#include #include NotifyCategoryDecl(android, EXPORT_CLASS, EXPORT_TEMPL); @@ -30,9 +31,12 @@ extern EXPORT_CLASS struct android_app* panda_android_app; extern jclass jni_PandaActivity; extern jmethodID jni_PandaActivity_readBitmapHeader; extern jmethodID jni_PandaActivity_readBitmap; +extern jmethodID jni_PandaActivity_showToast; extern jclass jni_BitmapFactory_Options; extern jfieldID jni_BitmapFactory_Options_outWidth; extern jfieldID jni_BitmapFactory_Options_outHeight; +EXPORT_CLASS void android_show_toast(ANativeActivity *activity, const string &message, int duration); + #endif diff --git a/panda/src/android/pview.cxx b/panda/src/android/pview.cxx index 9a29b94574..2b31bb7577 100644 --- a/panda/src/android/pview.cxx +++ b/panda/src/android/pview.cxx @@ -63,12 +63,16 @@ int main(int argc, char **argv) { window->enable_keyboard(); window->setup_trackball(); framework.get_models().instance_to(window->get_render()); - // if (argc < 2) { If we have no arguments, get that trusty old triangle - // out. window->load_default_model(framework.get_models()); } else { - // window->load_models(framework.get_models(), argc, argv); } - - window->load_model(framework.get_models(), "panda-model.egg"); - window->load_model(framework.get_models(), "panda-walk4.egg"); + if (argc < 2) { + // If we have no arguments, get that trusty old triangle + // out. + window->load_default_model(framework.get_models()); + } else { + if (!window->load_models(framework.get_models(), argc, argv)) { + framework.close_framework(); + return 1; + } + } window->loop_animations(hierarchy_match_flags); diff --git a/panda/src/android/pview_manifest.xml b/panda/src/android/pview_manifest.xml index 133fa523aa..6b14579a80 100644 --- a/panda/src/android/pview_manifest.xml +++ b/panda/src/android/pview_manifest.xml @@ -5,11 +5,15 @@ android:versionCode="1" android:versionName="1.0"> - - + + + + + + android:configChanges="orientation|keyboardHidden" + android:launchMode="singleInstance"> @@ -17,6 +21,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From d269f7c6c3bfd01d6eacfec5f58e9ebbe300f76a Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 6 Feb 2018 22:42:40 +0100 Subject: [PATCH 069/118] android: allow stdout/stderr capture using extra field on intent This allows launching the Panda apk from termux and getting command-line output back to termux. --- dtool/src/prc/androidLogStream.cxx | 1 + dtool/src/prc/notify.cxx | 3 +-- dtool/src/prc/notifyCategory.cxx | 6 ++++- panda/src/android/PandaActivity.java | 5 +++++ panda/src/android/android_main.cxx | 22 +++++++++++++++++++ .../express/virtualFileMountAndroidAsset.cxx | 2 +- 6 files changed, 35 insertions(+), 4 deletions(-) diff --git a/dtool/src/prc/androidLogStream.cxx b/dtool/src/prc/androidLogStream.cxx index b5512f9f52..6427b9163a 100644 --- a/dtool/src/prc/androidLogStream.cxx +++ b/dtool/src/prc/androidLogStream.cxx @@ -92,6 +92,7 @@ overflow(int ch) { */ void AndroidLogStream::AndroidLogStreamBuf:: write_char(char c) { + nout.put(c); if (c == '\n') { // Write a line to the log file. __android_log_write(_priority, _tag.c_str(), _data.c_str()); diff --git a/dtool/src/prc/notify.cxx b/dtool/src/prc/notify.cxx index f97b7d78ac..f74f55e596 100644 --- a/dtool/src/prc/notify.cxx +++ b/dtool/src/prc/notify.cxx @@ -335,9 +335,8 @@ assert_failure(const char *expression, int line, #ifdef ANDROID __android_log_assert("assert", "Panda3D", "Assertion failed: %s", message.c_str()); -#else - nout << "Assertion failed: " << message << "\n"; #endif + nout << "Assertion failed: " << message << "\n"; // This is redefined here, shadowing the defining in config_prc.h, so we can // guarantee it has already been constructed. diff --git a/dtool/src/prc/notifyCategory.cxx b/dtool/src/prc/notifyCategory.cxx index a6e0a0181c..432dfca159 100644 --- a/dtool/src/prc/notifyCategory.cxx +++ b/dtool/src/prc/notifyCategory.cxx @@ -64,7 +64,11 @@ out(NotifySeverity severity, bool prefix) const { // logging system. We use a special type of stream that redirects it to // Android's log system. if (prefix) { - return AndroidLogStream::out(severity) << *this << ": "; + if (severity == NS_info) { + return AndroidLogStream::out(severity) << *this << ": "; + } else { + return AndroidLogStream::out(severity) << *this << "(" << severity << "): "; + } } else { return AndroidLogStream::out(severity); } diff --git a/panda/src/android/PandaActivity.java b/panda/src/android/PandaActivity.java index 91e6c38533..c9985986f6 100644 --- a/panda/src/android/PandaActivity.java +++ b/panda/src/android/PandaActivity.java @@ -61,6 +61,11 @@ public class PandaActivity extends NativeActivity { return path; } + public String getIntentOutputPath() { + Intent intent = getIntent(); + return intent.getStringExtra("org.panda3d.OUTPUT_PATH"); + } + public String getCacheDirString() { return getCacheDir().toString(); } diff --git a/panda/src/android/android_main.cxx b/panda/src/android/android_main.cxx index c19ba7d58b..05a98b768c 100644 --- a/panda/src/android/android_main.cxx +++ b/panda/src/android/android_main.cxx @@ -158,6 +158,28 @@ void android_main(struct android_app* app) { } } + // Were we given an optional location to write the stdout/stderr streams? + methodID = env->GetMethodID(activity_class, "getIntentOutputPath", "()Ljava/lang/String;"); + jstring joutput_path = (jstring) env->CallObjectMethod(activity->clazz, methodID); + if (joutput_path != nullptr) { + const char *output_path = env->GetStringUTFChars(joutput_path, nullptr); + + if (output_path != nullptr && output_path[0] != 0) { + int fd = open(output_path, O_CREAT | O_TRUNC | O_WRONLY); + if (fd != -1) { + android_cat.info() + << "Writing standard output to file " << output_path << "\n"; + + dup2(fd, 1); + dup2(fd, 2); + } else { + android_cat.error() + << "Failed to open output path " << output_path << "\n"; + } + env->ReleaseStringUTFChars(joutput_path, output_path); + } + } + // Create bogus argc and argv for calling the main function. const char *argv[] = {"pview", nullptr, nullptr}; int argc = 1; diff --git a/panda/src/express/virtualFileMountAndroidAsset.cxx b/panda/src/express/virtualFileMountAndroidAsset.cxx index 3c85aaebb7..d7ffb6254a 100644 --- a/panda/src/express/virtualFileMountAndroidAsset.cxx +++ b/panda/src/express/virtualFileMountAndroidAsset.cxx @@ -78,7 +78,7 @@ is_regular_file(const Filename &file) const { AAsset* asset; asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_UNKNOWN); - express_cat.error() << "is_regular_file " << file << " - " << asset << "\n"; + //express_cat.error() << "is_regular_file " << file << " - " << asset << "\n"; if (asset == NULL) { return false; From c15c05f6424279cb943eeff98bbdc0afb87e3c38 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 6 Feb 2018 22:44:53 +0100 Subject: [PATCH 070/118] android: enable resume key events --- panda/src/androiddisplay/androidGraphicsWindow.cxx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/panda/src/androiddisplay/androidGraphicsWindow.cxx b/panda/src/androiddisplay/androidGraphicsWindow.cxx index d459f56339..8a66cde45a 100644 --- a/panda/src/androiddisplay/androidGraphicsWindow.cxx +++ b/panda/src/androiddisplay/androidGraphicsWindow.cxx @@ -396,8 +396,10 @@ create_surface() { */ void AndroidGraphicsWindow:: handle_command(struct android_app *app, int32_t command) { - AndroidGraphicsWindow* window = (AndroidGraphicsWindow*) app->userData; - window->ns_handle_command(command); + AndroidGraphicsWindow *window = (AndroidGraphicsWindow *)app->userData; + if (window != nullptr) { + window->ns_handle_command(command); + } } /** @@ -517,11 +519,15 @@ handle_key_event(const AInputEvent *event) { // Is it an up or down event? int32_t action = AKeyEvent_getAction(event); if (action == AKEY_EVENT_ACTION_DOWN) { - _input_devices[0].button_down(button); + if (AKeyEvent_getRepeatCount(event) > 0) { + _input_devices[0].button_resume_down(button); + } else { + _input_devices[0].button_down(button); + } } else if (action == AKEY_EVENT_ACTION_UP) { _input_devices[0].button_up(button); } - // TODO getRepeatCount, ACTION_MULTIPLE + // TODO AKEY_EVENT_ACTION_MULTIPLE return 1; } From e15cea433165fb5c500203ec21d9c0d14f7ba33c Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Wed, 7 Feb 2018 11:29:48 -0700 Subject: [PATCH 071/118] general: Remove more unnecessary +x UNIX modes --- contrib/src/panda3dtoolsgui/build_exe.bat | 0 direct/src/plugin_installer/FileAssociation.nsh | 0 direct/src/plugin_installer/VersionInfo.vbs | 0 direct/src/plugin_installer/p3d_installer.nsi | 0 dmodels/src/gui/radio_button_gui.egg | 0 dmodels/src/icons/dot_black.gif | Bin dmodels/src/icons/dot_blue.gif | Bin dmodels/src/icons/dot_green.gif | Bin dmodels/src/icons/dot_red.gif | Bin dmodels/src/icons/dot_white.gif | Bin dmodels/src/icons/folder.gif | Bin dmodels/src/icons/icon_arrowDown.egg | 0 dmodels/src/icons/icon_arrowDown_tall.egg | 0 dmodels/src/icons/icon_circle_cycle.egg | 0 dmodels/src/icons/icon_circle_no.egg | 0 dmodels/src/icons/icon_diamond.egg | 0 dmodels/src/icons/icon_gear1.egg | 0 dmodels/src/icons/icon_hourglass.egg | 0 dmodels/src/icons/icon_lightbulb.egg | 0 dmodels/src/icons/icon_lightning.egg | 0 dmodels/src/icons/icon_pacman.egg | 0 dmodels/src/icons/icon_plus.egg | 0 dmodels/src/icons/icon_ring.egg | 0 dmodels/src/icons/icon_star_5.egg | 0 dmodels/src/icons/icon_star_8.egg | 0 dmodels/src/icons/minusnode.gif | Bin dmodels/src/icons/openfolder.gif | Bin dmodels/src/icons/plusnode.gif | Bin dmodels/src/icons/python.gif | Bin dmodels/src/icons/sphere2.gif | Bin dmodels/src/icons/tk.gif | Bin dmodels/src/maps/Dirlight.png | Bin dmodels/src/maps/Pointlight.png | Bin dmodels/src/maps/Spotlight.png | Bin dmodels/src/maps/circle.png | Bin dmodels/src/maps/lightbulb.tif | Bin dmodels/src/maps/smiley.rgb | Bin dmodels/src/maps/square.tif | Bin dmodels/src/maps/square_opening.tif | Bin dmodels/src/maps/triangle.tif | Bin makepanda/confauto.in | 0 makepanda/config.in | 0 makepanda/installer.nsi | 0 makepanda/makechm.bat | 0 makepanda/makepanda.bat | 0 makepanda/makepanda.sln | 0 makepanda/makepanda.vcproj | 0 makepanda/panda-install.bmp | Bin models/environment.egg | 0 models/panda-model.egg | 0 models/panda-walk4.egg | 0 models/plugin_images/installer.bmp | Bin pandatool/src/mayaprogs/eggImportOptions.mel | 0 pandatool/src/scripts/MayaPandaTool.mel | 0 samples/music-box/music/musicbox.ogg | Bin samples/rocket-console/assets/console.rcss | 0 samples/rocket-console/assets/console.rml | 0 samples/rocket-console/assets/loading.rml | 0 samples/rocket-console/assets/rkt.rcss | 0 samples/rocket-console/assets/window.rcss | 0 samples/rocket-console/assets/window.rml | 0 61 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 contrib/src/panda3dtoolsgui/build_exe.bat mode change 100755 => 100644 direct/src/plugin_installer/FileAssociation.nsh mode change 100755 => 100644 direct/src/plugin_installer/VersionInfo.vbs mode change 100755 => 100644 direct/src/plugin_installer/p3d_installer.nsi mode change 100755 => 100644 dmodels/src/gui/radio_button_gui.egg mode change 100755 => 100644 dmodels/src/icons/dot_black.gif mode change 100755 => 100644 dmodels/src/icons/dot_blue.gif mode change 100755 => 100644 dmodels/src/icons/dot_green.gif mode change 100755 => 100644 dmodels/src/icons/dot_red.gif mode change 100755 => 100644 dmodels/src/icons/dot_white.gif mode change 100755 => 100644 dmodels/src/icons/folder.gif mode change 100755 => 100644 dmodels/src/icons/icon_arrowDown.egg mode change 100755 => 100644 dmodels/src/icons/icon_arrowDown_tall.egg mode change 100755 => 100644 dmodels/src/icons/icon_circle_cycle.egg mode change 100755 => 100644 dmodels/src/icons/icon_circle_no.egg mode change 100755 => 100644 dmodels/src/icons/icon_diamond.egg mode change 100755 => 100644 dmodels/src/icons/icon_gear1.egg mode change 100755 => 100644 dmodels/src/icons/icon_hourglass.egg mode change 100755 => 100644 dmodels/src/icons/icon_lightbulb.egg mode change 100755 => 100644 dmodels/src/icons/icon_lightning.egg mode change 100755 => 100644 dmodels/src/icons/icon_pacman.egg mode change 100755 => 100644 dmodels/src/icons/icon_plus.egg mode change 100755 => 100644 dmodels/src/icons/icon_ring.egg mode change 100755 => 100644 dmodels/src/icons/icon_star_5.egg mode change 100755 => 100644 dmodels/src/icons/icon_star_8.egg mode change 100755 => 100644 dmodels/src/icons/minusnode.gif mode change 100755 => 100644 dmodels/src/icons/openfolder.gif mode change 100755 => 100644 dmodels/src/icons/plusnode.gif mode change 100755 => 100644 dmodels/src/icons/python.gif mode change 100755 => 100644 dmodels/src/icons/sphere2.gif mode change 100755 => 100644 dmodels/src/icons/tk.gif mode change 100755 => 100644 dmodels/src/maps/Dirlight.png mode change 100755 => 100644 dmodels/src/maps/Pointlight.png mode change 100755 => 100644 dmodels/src/maps/Spotlight.png mode change 100755 => 100644 dmodels/src/maps/circle.png mode change 100755 => 100644 dmodels/src/maps/lightbulb.tif mode change 100755 => 100644 dmodels/src/maps/smiley.rgb mode change 100755 => 100644 dmodels/src/maps/square.tif mode change 100755 => 100644 dmodels/src/maps/square_opening.tif mode change 100755 => 100644 dmodels/src/maps/triangle.tif mode change 100755 => 100644 makepanda/confauto.in mode change 100755 => 100644 makepanda/config.in mode change 100755 => 100644 makepanda/installer.nsi mode change 100755 => 100644 makepanda/makechm.bat mode change 100755 => 100644 makepanda/makepanda.bat mode change 100755 => 100644 makepanda/makepanda.sln mode change 100755 => 100644 makepanda/makepanda.vcproj mode change 100755 => 100644 makepanda/panda-install.bmp mode change 100755 => 100644 models/environment.egg mode change 100755 => 100644 models/panda-model.egg mode change 100755 => 100644 models/panda-walk4.egg mode change 100755 => 100644 models/plugin_images/installer.bmp mode change 100755 => 100644 pandatool/src/mayaprogs/eggImportOptions.mel mode change 100755 => 100644 pandatool/src/scripts/MayaPandaTool.mel mode change 100755 => 100644 samples/music-box/music/musicbox.ogg mode change 100755 => 100644 samples/rocket-console/assets/console.rcss mode change 100755 => 100644 samples/rocket-console/assets/console.rml mode change 100755 => 100644 samples/rocket-console/assets/loading.rml mode change 100755 => 100644 samples/rocket-console/assets/rkt.rcss mode change 100755 => 100644 samples/rocket-console/assets/window.rcss mode change 100755 => 100644 samples/rocket-console/assets/window.rml diff --git a/contrib/src/panda3dtoolsgui/build_exe.bat b/contrib/src/panda3dtoolsgui/build_exe.bat old mode 100755 new mode 100644 diff --git a/direct/src/plugin_installer/FileAssociation.nsh b/direct/src/plugin_installer/FileAssociation.nsh old mode 100755 new mode 100644 diff --git a/direct/src/plugin_installer/VersionInfo.vbs b/direct/src/plugin_installer/VersionInfo.vbs old mode 100755 new mode 100644 diff --git a/direct/src/plugin_installer/p3d_installer.nsi b/direct/src/plugin_installer/p3d_installer.nsi old mode 100755 new mode 100644 diff --git a/dmodels/src/gui/radio_button_gui.egg b/dmodels/src/gui/radio_button_gui.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/dot_black.gif b/dmodels/src/icons/dot_black.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/dot_blue.gif b/dmodels/src/icons/dot_blue.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/dot_green.gif b/dmodels/src/icons/dot_green.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/dot_red.gif b/dmodels/src/icons/dot_red.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/dot_white.gif b/dmodels/src/icons/dot_white.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/folder.gif b/dmodels/src/icons/folder.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_arrowDown.egg b/dmodels/src/icons/icon_arrowDown.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_arrowDown_tall.egg b/dmodels/src/icons/icon_arrowDown_tall.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_circle_cycle.egg b/dmodels/src/icons/icon_circle_cycle.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_circle_no.egg b/dmodels/src/icons/icon_circle_no.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_diamond.egg b/dmodels/src/icons/icon_diamond.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_gear1.egg b/dmodels/src/icons/icon_gear1.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_hourglass.egg b/dmodels/src/icons/icon_hourglass.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_lightbulb.egg b/dmodels/src/icons/icon_lightbulb.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_lightning.egg b/dmodels/src/icons/icon_lightning.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_pacman.egg b/dmodels/src/icons/icon_pacman.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_plus.egg b/dmodels/src/icons/icon_plus.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_ring.egg b/dmodels/src/icons/icon_ring.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_star_5.egg b/dmodels/src/icons/icon_star_5.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/icon_star_8.egg b/dmodels/src/icons/icon_star_8.egg old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/minusnode.gif b/dmodels/src/icons/minusnode.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/openfolder.gif b/dmodels/src/icons/openfolder.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/plusnode.gif b/dmodels/src/icons/plusnode.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/python.gif b/dmodels/src/icons/python.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/sphere2.gif b/dmodels/src/icons/sphere2.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/icons/tk.gif b/dmodels/src/icons/tk.gif old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/Dirlight.png b/dmodels/src/maps/Dirlight.png old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/Pointlight.png b/dmodels/src/maps/Pointlight.png old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/Spotlight.png b/dmodels/src/maps/Spotlight.png old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/circle.png b/dmodels/src/maps/circle.png old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/lightbulb.tif b/dmodels/src/maps/lightbulb.tif old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/smiley.rgb b/dmodels/src/maps/smiley.rgb old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/square.tif b/dmodels/src/maps/square.tif old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/square_opening.tif b/dmodels/src/maps/square_opening.tif old mode 100755 new mode 100644 diff --git a/dmodels/src/maps/triangle.tif b/dmodels/src/maps/triangle.tif old mode 100755 new mode 100644 diff --git a/makepanda/confauto.in b/makepanda/confauto.in old mode 100755 new mode 100644 diff --git a/makepanda/config.in b/makepanda/config.in old mode 100755 new mode 100644 diff --git a/makepanda/installer.nsi b/makepanda/installer.nsi old mode 100755 new mode 100644 diff --git a/makepanda/makechm.bat b/makepanda/makechm.bat old mode 100755 new mode 100644 diff --git a/makepanda/makepanda.bat b/makepanda/makepanda.bat old mode 100755 new mode 100644 diff --git a/makepanda/makepanda.sln b/makepanda/makepanda.sln old mode 100755 new mode 100644 diff --git a/makepanda/makepanda.vcproj b/makepanda/makepanda.vcproj old mode 100755 new mode 100644 diff --git a/makepanda/panda-install.bmp b/makepanda/panda-install.bmp old mode 100755 new mode 100644 diff --git a/models/environment.egg b/models/environment.egg old mode 100755 new mode 100644 diff --git a/models/panda-model.egg b/models/panda-model.egg old mode 100755 new mode 100644 diff --git a/models/panda-walk4.egg b/models/panda-walk4.egg old mode 100755 new mode 100644 diff --git a/models/plugin_images/installer.bmp b/models/plugin_images/installer.bmp old mode 100755 new mode 100644 diff --git a/pandatool/src/mayaprogs/eggImportOptions.mel b/pandatool/src/mayaprogs/eggImportOptions.mel old mode 100755 new mode 100644 diff --git a/pandatool/src/scripts/MayaPandaTool.mel b/pandatool/src/scripts/MayaPandaTool.mel old mode 100755 new mode 100644 diff --git a/samples/music-box/music/musicbox.ogg b/samples/music-box/music/musicbox.ogg old mode 100755 new mode 100644 diff --git a/samples/rocket-console/assets/console.rcss b/samples/rocket-console/assets/console.rcss old mode 100755 new mode 100644 diff --git a/samples/rocket-console/assets/console.rml b/samples/rocket-console/assets/console.rml old mode 100755 new mode 100644 diff --git a/samples/rocket-console/assets/loading.rml b/samples/rocket-console/assets/loading.rml old mode 100755 new mode 100644 diff --git a/samples/rocket-console/assets/rkt.rcss b/samples/rocket-console/assets/rkt.rcss old mode 100755 new mode 100644 diff --git a/samples/rocket-console/assets/window.rcss b/samples/rocket-console/assets/window.rcss old mode 100755 new mode 100644 diff --git a/samples/rocket-console/assets/window.rml b/samples/rocket-console/assets/window.rml old mode 100755 new mode 100644 From bdb74b1e30bfdad52635f0fe20d940451f38d1b7 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 9 Feb 2018 19:49:02 +0100 Subject: [PATCH 072/118] makepanda: add code to build an Android package with --installer --- makepanda/makepanda.py | 113 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 2f21483e78..b8aa622429 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -7444,6 +7444,117 @@ def MakeInstallerFreeBSD(): WriteFile("+MANIFEST", manifest_txt) oscmd("pkg create -p pkg-plist -r %s -m . -o . %s" % (os.path.abspath("targetroot"), "--verbose" if GetVerbose() else "--quiet")) +def MakeInstallerAndroid(): + oscmd("rm -rf apkroot") + oscmd("mkdir apkroot") + + # Also remove the temporary apks. + apk_unaligned = os.path.join(GetOutputDir(), "tmp", "panda3d-unaligned.apk") + apk_unsigned = os.path.join(GetOutputDir(), "tmp", "panda3d-unsigned.apk") + if os.path.exists(apk_unaligned): + os.unlink(apk_unaligned) + if os.path.exists(apk_unsigned): + os.unlink(apk_unsigned) + + # Compile the Java classes into a Dalvik executable. + dx_cmd = "dx --dex --output=apkroot/classes.dex " + if GetOptimize() <= 2: + dx_cmd += "--debug " + if GetVerbose(): + dx_cmd += "--verbose " + if "ANDROID_API" in SDK: + dx_cmd += "--min-sdk-version=%d " % (SDK["ANDROID_API"]) + dx_cmd += os.path.join(GetOutputDir(), "classes") + oscmd(dx_cmd) + + # Copy the libraries one by one. In case of library dependencies, strip + # off any suffix (eg. libfile.so.1.0), as Android does not support them. + source_dir = os.path.join(GetOutputDir(), "lib") + target_dir = os.path.join("apkroot", "lib", SDK["ANDROID_ABI"]) + oscmd("mkdir -p %s" % (target_dir)) + + # Determine the library directories we should look in. + libpath = [source_dir] + for dir in os.environ.get("LD_LIBRARY_PATH", "").split(':'): + dir = os.path.expandvars(dir) + dir = os.path.expanduser(dir) + if os.path.isdir(dir): + dir = os.path.realpath(dir) + if not dir.startswith("/system") and not dir.startswith("/vendor"): + libpath.append(dir) + + def copy_library(source, base): + # Copy file to destination, stripping version suffix. + target = os.path.join(target_dir, base) + if not target.endswith('.so'): + target = target.rpartition('.so.')[0] + '.so' + + if os.path.isfile(target): + # Already processed. + return + + oscmd("cp %s %s" % (source, target)) + + # Walk through the library dependencies. + oscmd("ldd %s | grep .so > %s/tmp/otool-libs.txt" % (target, GetOutputDir()), True) + for line in open(GetOutputDir() + "/tmp/otool-libs.txt", "r"): + line = line.strip() + if not line: + continue + if '.so.' in line: + dep = line.rpartition('.so.')[0] + '.so' + oscmd("patchelf --replace-needed %s %s %s" % (line, dep, target)) + else: + dep = line + + # Find it on the LD_LIBRARY_PATH. + for dir in libpath: + fulldep = os.path.join(dir, dep) + if os.path.isfile(fulldep): + copy_library(os.path.realpath(fulldep), dep) + break + + for base in os.listdir(source_dir): + if not base.startswith('lib'): + continue + if not base.endswith('.so') and '.so.' not in base: + continue + + source = os.path.join(source_dir, base) + if os.path.islink(source): + continue + copy_library(source, base) + + # Copy the models as well. + oscmd("mkdir apkroot/assets") + oscmd("cp -R %s apkroot/assets/models" % (os.path.join(GetOutputDir(), "models"))) + + # Make an empty res folder. It's needed for the apk to be installable, apparently. + oscmd("mkdir apkroot/res") + + # Now package up the application + oscmd("cp panda/src/android/pview_manifest.xml apkroot/AndroidManifest.xml") + aapt_cmd = "aapt package" + aapt_cmd += " -F %s" % (apk_unaligned) + aapt_cmd += " -M apkroot/AndroidManifest.xml" + aapt_cmd += " -A apkroot/assets -S apkroot/res" + aapt_cmd += " -I $PREFIX/share/aapt/android.jar" + oscmd(aapt_cmd) + + # And add all the libraries to it. + oscmd("cd apkroot && aapt add ../%s classes.dex lib/%s/lib*.so" % (apk_unaligned, SDK["ANDROID_ABI"])) + + # Now align the .apk, which is necessary for Android to load it. + oscmd("zipalign -v -p 4 %s %s" % (apk_unaligned, apk_unsigned)) + + # Finally, sign it using a debug key. This is generated if it doesn't exist. + oscmd("apksigner debug.ks %s panda3d.apk" % (apk_unsigned)) + + # Clean up. + oscmd("rm -rf apkroot") + os.unlink(apk_unaligned) + os.unlink(apk_unsigned) + try: if INSTALLER: ProgressOutput(100.0, "Building installer") @@ -7478,6 +7589,8 @@ try: MakeInstallerOSX() elif (target == 'freebsd'): MakeInstallerFreeBSD() + elif (target == 'android'): + MakeInstallerAndroid() else: exit("Do not know how to make an installer for this platform") From 730279531a8fa77ba46d19e814a4419053371316 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 10 Feb 2018 12:13:05 +0100 Subject: [PATCH 073/118] makepanda: build on Intel Android machines --- dtool/src/dtoolbase/dtool_platform.h | 2 ++ makepanda/makepanda.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/dtool/src/dtoolbase/dtool_platform.h b/dtool/src/dtoolbase/dtool_platform.h index 9037632738..2a6ed12a9b 100644 --- a/dtool/src/dtoolbase/dtool_platform.h +++ b/dtool/src/dtoolbase/dtool_platform.h @@ -57,6 +57,8 @@ #define DTOOL_PLATFORM "android_arm" #elif defined(__mips__) #define DTOOL_PLATFORM "android_mips" +#elif defined(__x86_64) +#define DTOOL_PLATFORM "android_amd64" #elif defined(__i386__) #define DTOOL_PLATFORM "android_i386" #endif diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index b8aa622429..9b653bfc1c 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -1291,9 +1291,11 @@ def CompileCxx(obj,src,opts): cmd += ' -fintegrated-as' elif arch == 'x86': cmd += ' -target i686-none-linux-android' + cmd += ' -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32' cmd += ' -mstackrealign' elif arch == 'x86_64': cmd += ' -target x86_64-none-linux-android' + cmd += ' -march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel' cmd += " -Wa,--noexecstack" From b4ad0a69a0f0b71e65584298a60eaa8177813f8e Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 11 Feb 2018 00:55:04 +0100 Subject: [PATCH 074/118] makepanda: automatic dependency scanning for Java sources --- makepanda/makepandacore.py | 54 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index bc0cdc7549..cd9770c1e9 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -764,6 +764,34 @@ def CxxGetIncludes(path): CXXINCLUDECACHE[path] = [date, include] return include +JAVAIMPORTCACHE = {} + +global JavaImportRegex +JavaImportRegex = re.compile('[ \t\r\n;]import[ \t]+([a-zA-Z][^;]+)[ \t\r\n]*;') + +def JavaGetImports(path): + date = GetTimestamp(path) + if path in JAVAIMPORTCACHE: + cached = JAVAIMPORTCACHE[path] + if cached[0] == date: + return cached[1] + try: + source = open(path, 'r').read() + except: + exit("Cannot open source file \"" + path + "\" for reading.") + + imports = [] + try: + for match in JavaImportRegex.finditer(source, 0): + impname = match.group(1) + imports.append(impname.strip()) + except: + print("Failed to determine dependencies of \"" + path +"\".") + raise + + JAVAIMPORTCACHE[path] = [date, imports] + return imports + ######################################################################## ## ## SaveDependencyCache / LoadDependencyCache @@ -862,6 +890,13 @@ def CxxFindHeader(srcfile, incfile, ipath): if GetTimestamp(full) > 0: return full return 0 +def JavaFindClasses(impspec, clspath): + path = clspath + '/' + impspec.replace('.', '/') + '.class' + if '*' in path: + return glob.glob(path) + else: + return [path] + ######################################################################## ## ## CxxCalcDependencies(srcfile, ipath, ignore) @@ -894,6 +929,22 @@ def CxxCalcDependencies(srcfile, ipath, ignore): CxxDependencyCache[srcfile] = result return result +global JavaDependencyCache +JavaDependencyCache = {} + +def JavaCalcDependencies(srcfile, clspath): + if srcfile in JavaDependencyCache: + return JavaDependencyCache[srcfile] + + deps = set((srcfile,)) + JavaDependencyCache[srcfile] = deps + + imports = JavaGetImports(srcfile) + for impspec in imports: + for cls in JavaFindClasses(impspec, clspath): + deps.add(cls) + return deps + ######################################################################## ## ## Registry Key Handling @@ -3314,6 +3365,9 @@ def TargetAdd(target, dummy=0, opts=[], input=[], dep=[], ipath=None, winrc=None if (SUFFIX_INC.count(suffix)): for d in CxxCalcDependencies(fullinput, ipath, []): t.deps[d] = 1 + elif suffix == '.java': + for d in JavaCalcDependencies(fullinput, OUTPUTDIR + "/classes"): + t.deps[d] = 1 # If we are linking statically, add the source DLL's dynamic dependencies. if GetLinkAllStatic() and ORIG_EXT[fullinput] == '.lib' and fullinput in TARGET_TABLE: From c41b694eb3e83cdd52e337a303733049de80172e Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 11 Feb 2018 00:56:25 +0100 Subject: [PATCH 075/118] makepanda: detect public system libraries when building on Android --- makepanda/makepanda.py | 11 ++++------- makepanda/makepandacore.py | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 9b653bfc1c..69c8c93a37 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -821,6 +821,9 @@ if (COMPILER=="GCC"): SmartPkgEnable("FREETYPE", "freetype2", ("freetype"), ("freetype2", "freetype2/freetype/freetype.h")) SmartPkgEnable("HARFBUZZ", "harfbuzz", ("harfbuzz"), ("harfbuzz", "harfbuzz/hb-ft.h")) SmartPkgEnable("GL", "gl", ("GL"), ("GL/gl.h"), framework = "OpenGL") + SmartPkgEnable("GLES", "glesv1_cm", ("GLESv1_CM"), ("GLES/gl.h"), framework = "OpenGLES") + SmartPkgEnable("GLES2", "glesv2", ("GLESv2"), ("GLES2/gl2.h")) #framework = "OpenGLES"? + SmartPkgEnable("EGL", "egl", ("EGL"), ("EGL/egl.h")) SmartPkgEnable("NVIDIACG", "", ("Cg"), "Cg/cg.h", framework = "Cg") SmartPkgEnable("ODE", "", ("ode"), "ode/ode.h", tool = "ode-config") SmartPkgEnable("OPENAL", "openal", ("openal"), "AL/al.h", framework = "OpenAL") @@ -830,16 +833,10 @@ if (COMPILER=="GCC"): SmartPkgEnable("VRPN", "", ("vrpn", "quat"), ("vrpn", "quat.h", "vrpn/vrpn_Types.h")) SmartPkgEnable("BULLET", "bullet", ("BulletSoftBody", "BulletDynamics", "BulletCollision", "LinearMath"), ("bullet", "bullet/btBulletDynamicsCommon.h")) SmartPkgEnable("VORBIS", "vorbisfile",("vorbisfile", "vorbis", "ogg"), ("ogg/ogg.h", "vorbis/vorbisfile.h")) - SmartPkgEnable("OPUS", "opusfile", ("opusfile", "opus", "ogg"), ("ogg/ogg.h", "opus/opusfile.h")) + SmartPkgEnable("OPUS", "opusfile", ("opusfile", "opus", "ogg"), ("ogg/ogg.h", "opus/opusfile.h", "opus")) SmartPkgEnable("JPEG", "", ("jpeg"), "jpeglib.h") SmartPkgEnable("PNG", "libpng", ("png"), "png.h", tool = "libpng-config") - # On Android, these are system libraries. - if GetHost() != "android": - SmartPkgEnable("GLES", "glesv1_cm", (), ("GLES/gl.h"), framework = "OpenGLES") - SmartPkgEnable("GLES2", "glesv2", (), ("GLES2/gl2.h")) #framework = "OpenGLES"? - SmartPkgEnable("EGL", "egl", (), ("EGL/egl.h")) - if not PkgSkip("FFMPEG"): if GetTarget() == "darwin": LibName("FFMPEG", "-Wl,-read_only_relocs,suppress") diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index cd9770c1e9..0bce3f4450 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -59,6 +59,13 @@ else: # case. host_64 = (platform.architecture()[0] == '64bit') +# On Android, get a list of all the public system libraries. +ANDROID_SYS_LIBS = [] +if os.path.exists("/etc/public.libraries.txt"): + for line in open("/etc/public.libraries.txt", "r"): + line = line.strip() + ANDROID_SYS_LIBS.append(line) + ######################################################################## ## ## Visual C++ Version (MSVC) and Visual Studio Information Map @@ -1568,7 +1575,14 @@ def LocateLibrary(lib, lpath=[], prefer_static=False): return None def SystemLibraryExists(lib): - return LocateLibrary(lib, SYS_LIB_DIRS) is not None + result = LocateLibrary(lib, SYS_LIB_DIRS) + if result is not None: + return True + + if GetHost() == "android" and GetTarget() == "android": + return ('lib%s.so' % lib) in ANDROID_SYS_LIBS + + return False def ChooseLib(libs, thirdparty=None): """ Chooses a library from the parameters, in order of preference. Returns the first if none of them were found. """ From ab7dbebf3f224447fe79f9cd92239034cd877126 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Wed, 14 Feb 2018 17:25:37 -0700 Subject: [PATCH 076/118] general: Remove lingering references to Helix Support for Helix has been dropped long ago; it's very unlikely to return. See also de4280ea209d567eedfa08084da0dea761ad5917. --- dtool/src/parser-inc/MainHelix.h | 9 --------- dtool/src/parser-inc/hxcom.h | 6 ------ dtool/src/parser-inc/hxcomm.h | 6 ------ dtool/src/parser-inc/hxcore.h | 6 ------ dtool/src/parser-inc/hxengin.h | 6 ------ dtool/src/parser-inc/hxerror.h | 6 ------ dtool/src/parser-inc/hxfiles.h | 6 ------ dtool/src/parser-inc/hxtbuf.h | 6 ------ dtool/src/parser-inc/hxtbuff.h | 6 ------ dtool/src/parser-inc/hxwin.h | 6 ------ panda/src/pandabase/pandasymbols.h | 3 --- 11 files changed, 66 deletions(-) delete mode 100644 dtool/src/parser-inc/MainHelix.h delete mode 100644 dtool/src/parser-inc/hxcom.h delete mode 100644 dtool/src/parser-inc/hxcomm.h delete mode 100644 dtool/src/parser-inc/hxcore.h delete mode 100644 dtool/src/parser-inc/hxengin.h delete mode 100644 dtool/src/parser-inc/hxerror.h delete mode 100644 dtool/src/parser-inc/hxfiles.h delete mode 100644 dtool/src/parser-inc/hxtbuf.h delete mode 100644 dtool/src/parser-inc/hxtbuff.h delete mode 100644 dtool/src/parser-inc/hxwin.h diff --git a/dtool/src/parser-inc/MainHelix.h b/dtool/src/parser-inc/MainHelix.h deleted file mode 100644 index deeefca994..0000000000 --- a/dtool/src/parser-inc/MainHelix.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef MAINHELIX_H -#define MAINHELIX_H - -// This file is a stub header file. -class DLLAccess {}; -class IHXClientEngine {}; -class IHXPlayer {}; - -#endif diff --git a/dtool/src/parser-inc/hxcom.h b/dtool/src/parser-inc/hxcom.h deleted file mode 100644 index 8c733fddd3..0000000000 --- a/dtool/src/parser-inc/hxcom.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXCOM_H -#define HXCOM_H - -// This file is a stub header file. - -#endif diff --git a/dtool/src/parser-inc/hxcomm.h b/dtool/src/parser-inc/hxcomm.h deleted file mode 100644 index 5e1137c6d7..0000000000 --- a/dtool/src/parser-inc/hxcomm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXCOMM_H -#define HXCOMM_H - -// This file is a stub header file. - -#endif diff --git a/dtool/src/parser-inc/hxcore.h b/dtool/src/parser-inc/hxcore.h deleted file mode 100644 index 4848d39d34..0000000000 --- a/dtool/src/parser-inc/hxcore.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXCORE_H -#define HXCORE_H - -// This file is a stub header file. - -#endif diff --git a/dtool/src/parser-inc/hxengin.h b/dtool/src/parser-inc/hxengin.h deleted file mode 100644 index 0b6467baae..0000000000 --- a/dtool/src/parser-inc/hxengin.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXENGIN_H -#define HXENGIN_H - -// This file is a stub header file. - -#endif diff --git a/dtool/src/parser-inc/hxerror.h b/dtool/src/parser-inc/hxerror.h deleted file mode 100644 index b3cf207ade..0000000000 --- a/dtool/src/parser-inc/hxerror.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXERROR_H -#define HXERROR_H - -// This file is a stub header file. - -#endif diff --git a/dtool/src/parser-inc/hxfiles.h b/dtool/src/parser-inc/hxfiles.h deleted file mode 100644 index 4616b8bd11..0000000000 --- a/dtool/src/parser-inc/hxfiles.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXFILES_H -#define HXFILES_H - -// This file is a stub header file. - -#endif diff --git a/dtool/src/parser-inc/hxtbuf.h b/dtool/src/parser-inc/hxtbuf.h deleted file mode 100644 index 12210512a1..0000000000 --- a/dtool/src/parser-inc/hxtbuf.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXTBUF_H -#define HXTBUF_H - -// This file is a stub header file. - -#endif diff --git a/dtool/src/parser-inc/hxtbuff.h b/dtool/src/parser-inc/hxtbuff.h deleted file mode 100644 index 5ba15806f5..0000000000 --- a/dtool/src/parser-inc/hxtbuff.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXTBUFF_H -#define HXTBUFF_H - -// This file is a stub header file. - -#endif diff --git a/dtool/src/parser-inc/hxwin.h b/dtool/src/parser-inc/hxwin.h deleted file mode 100644 index b8601c2a63..0000000000 --- a/dtool/src/parser-inc/hxwin.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef HXWIN_H -#define HXWIN_H - -// This file is a stub header file. - -#endif diff --git a/panda/src/pandabase/pandasymbols.h b/panda/src/pandabase/pandasymbols.h index 4e7389dadc..cc02ff0e12 100644 --- a/panda/src/pandabase/pandasymbols.h +++ b/panda/src/pandabase/pandasymbols.h @@ -429,7 +429,4 @@ #define EXPCL_PANDA_PANDABASE EXPCL_PANDA #define EXPTP_PANDA_PANDABASE EXPTP_PANDA -#define EXPCL_PANDA_HELIX EXPCL_PANDA -#define EXPTP_PANDA_HELIX EXPTP_PANDA - #endif From 854d736882516400a6f8ecb172aa0374275dfcf4 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 17 Feb 2018 17:49:36 +0100 Subject: [PATCH 077/118] pgraph: fix alignment error in 32-bit Windows with Eigen Fixes #251 --- panda/src/pgraph/nodePath.cxx | 22 +++++++++++++++++++++- panda/src/pgraph/nodePath.h | 3 ++- panda/src/pgraph/shaderAttrib.cxx | 17 ++++++++++++++++- panda/src/pgraph/shaderAttrib.h | 3 ++- 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index c9ccfd7c90..71bbb3a561 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -3262,7 +3262,7 @@ get_shader() const { * */ void NodePath:: -set_shader_input(ShaderInput inp) { +set_shader_input(const ShaderInput &inp) { nassertv_always(!is_empty()); PandaNode *pnode = node(); @@ -3278,6 +3278,26 @@ set_shader_input(ShaderInput inp) { } } +/** + * + */ +void NodePath:: +set_shader_input(ShaderInput &&inp) { + nassertv_always(!is_empty()); + + PandaNode *pnode = node(); + const RenderAttrib *attrib = + pnode->get_attrib(ShaderAttrib::get_class_slot()); + if (attrib != nullptr) { + const ShaderAttrib *sa = (const ShaderAttrib *)attrib; + pnode->set_attrib(sa->set_shader_input(move(inp))); + } else { + // Create a new ShaderAttrib for this node. + CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make()); + pnode->set_attrib(sa->set_shader_input(move(inp))); + } +} + /** * */ diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index 617aac46a5..c5a3902c32 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -629,7 +629,8 @@ PUBLISHED: void set_shader_auto(BitMask32 shader_switch, int priority=0); void clear_shader(); - void set_shader_input(ShaderInput input); + void set_shader_input(const ShaderInput &input); + void set_shader_input(ShaderInput &&input); INLINE void set_shader_input(CPT_InternalName id, Texture *tex, const SamplerState &sampler, int priority=0); INLINE void set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0); diff --git a/panda/src/pgraph/shaderAttrib.cxx b/panda/src/pgraph/shaderAttrib.cxx index 711e886828..130d85c1b8 100644 --- a/panda/src/pgraph/shaderAttrib.cxx +++ b/panda/src/pgraph/shaderAttrib.cxx @@ -195,7 +195,22 @@ clear_flag(int flag) const { * */ CPT(RenderAttrib) ShaderAttrib:: -set_shader_input(ShaderInput input) const { +set_shader_input(const ShaderInput &input) const { + ShaderAttrib *result = new ShaderAttrib(*this); + Inputs::iterator i = result->_inputs.find(input.get_name()); + if (i == result->_inputs.end()) { + result->_inputs.insert(Inputs::value_type(input.get_name(), input)); + } else { + i->second = input; + } + return return_new(result); +} + +/** + * + */ +CPT(RenderAttrib) ShaderAttrib:: +set_shader_input(ShaderInput &&input) const { ShaderAttrib *result = new ShaderAttrib(*this); Inputs::iterator i = result->_inputs.find(input.get_name()); if (i == result->_inputs.end()) { diff --git a/panda/src/pgraph/shaderAttrib.h b/panda/src/pgraph/shaderAttrib.h index cb1eec37d9..02a3189233 100644 --- a/panda/src/pgraph/shaderAttrib.h +++ b/panda/src/pgraph/shaderAttrib.h @@ -71,7 +71,8 @@ PUBLISHED: CPT(RenderAttrib) clear_shader() const; // Shader Inputs - CPT(RenderAttrib) set_shader_input(ShaderInput input) const; + CPT(RenderAttrib) set_shader_input(const ShaderInput &input) const; + CPT(RenderAttrib) set_shader_input(ShaderInput &&input) const; public: INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, Texture *tex, int priority=0) const; From 6bd19768923d177bbb4fda6bca465e285ea64923 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 17 Feb 2018 18:40:08 +0100 Subject: [PATCH 078/118] android: support writing png/jpg/webp via android.graphics.Bitmap --- makepanda/makepanda.py | 3 +- panda/src/android/NativeOStream.java | 52 +++++++ panda/src/android/PandaActivity.java | 26 ++++ panda/src/android/config_android.cxx | 57 ++++++- panda/src/android/config_android.h | 2 + panda/src/android/jni_NativeOStream.cxx | 54 +++++++ panda/src/android/p3android_composite1.cxx | 4 +- panda/src/android/pnmFileTypeAndroid.cxx | 64 ++++---- panda/src/android/pnmFileTypeAndroid.h | 38 +++-- .../src/android/pnmFileTypeAndroidWriter.cxx | 146 ++++++++++++++++++ panda/src/android/pview_manifest.xml | 1 + panda/src/pnmimage/pnmFileTypeRegistry.cxx | 48 +++++- panda/src/pnmimage/pnmFileTypeRegistry.h | 1 + 13 files changed, 426 insertions(+), 70 deletions(-) create mode 100644 panda/src/android/NativeOStream.java create mode 100644 panda/src/android/jni_NativeOStream.cxx create mode 100644 panda/src/android/pnmFileTypeAndroidWriter.cxx diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 69c8c93a37..ad974d38d0 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -5100,7 +5100,8 @@ if (not RTDIST and not RUNTIME and PkgSkip("PVIEW")==0 and GetTarget() != 'andro if (not RUNTIME and GetTarget() == 'android'): OPTS=['DIR:panda/src/android'] TargetAdd('org/panda3d/android/NativeIStream.class', opts=OPTS, input='NativeIStream.java') - TargetAdd('org/panda3d/android/PandaActivity.class', opts=OPTS, input='PandaActivity.java', dep='org/panda3d/android/NativeIStream.class') + TargetAdd('org/panda3d/android/NativeOStream.class', opts=OPTS, input='NativeOStream.java') + TargetAdd('org/panda3d/android/PandaActivity.class', opts=OPTS, input='PandaActivity.java') TargetAdd('p3android_composite1.obj', opts=OPTS, input='p3android_composite1.cxx') TargetAdd('libp3android.dll', input='p3android_composite1.obj') diff --git a/panda/src/android/NativeOStream.java b/panda/src/android/NativeOStream.java new file mode 100644 index 0000000000..ddd76e485a --- /dev/null +++ b/panda/src/android/NativeOStream.java @@ -0,0 +1,52 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file NativeOStream.java + * @author rdb + * @date 2018-02-10 + */ + +package org.panda3d.android; + +import java.io.OutputStream; + +/** + * An implementation of OutputStream that puts its data into a C++ ostream + * pointer, passed as long. + */ +public class NativeOStream extends OutputStream { + private long streamPtr = 0; + + public NativeOStream(long ptr) { + streamPtr = ptr; + } + + @Override + public void flush() { + nativeFlush(streamPtr); + } + + @Override + public void write(int b) { + nativePut(streamPtr, b); + } + + @Override + public void write(byte[] buffer) { + nativeWrite(streamPtr, buffer, 0, buffer.length); + } + + @Override + public void write(byte[] buffer, int offset, int length) { + nativeWrite(streamPtr, buffer, offset, length); + } + + private static native void nativeFlush(long ptr); + private static native void nativePut(long ptr, int b); + private static native void nativeWrite(long ptr, byte[] buffer, int offset, int length); +} diff --git a/panda/src/android/PandaActivity.java b/panda/src/android/PandaActivity.java index c9985986f6..a4413a2816 100644 --- a/panda/src/android/PandaActivity.java +++ b/panda/src/android/PandaActivity.java @@ -20,12 +20,29 @@ import android.widget.Toast; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import org.panda3d.android.NativeIStream; +import org.panda3d.android.NativeOStream; /** * The entry point for a Panda-based activity. Loads the Panda libraries and * also provides some utility functions. */ public class PandaActivity extends NativeActivity { + private static final Bitmap.Config sConfigs[] = { + null, + Bitmap.Config.ALPHA_8, + null, + Bitmap.Config.RGB_565, + Bitmap.Config.ARGB_4444, + Bitmap.Config.ARGB_8888, + null, //Bitmap.Config.RGBA_F16, + null, //Bitmap.Config.HARDWARE, + }; + private static final Bitmap.CompressFormat sFormats[] = { + Bitmap.CompressFormat.JPEG, + Bitmap.CompressFormat.PNG, + Bitmap.CompressFormat.WEBP, + }; + protected static BitmapFactory.Options readBitmapSize(long istreamPtr) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; @@ -44,6 +61,15 @@ public class PandaActivity extends NativeActivity { return BitmapFactory.decodeStream(stream, null, options); } + protected static Bitmap createBitmap(int width, int height, int config, boolean hasAlpha) { + return Bitmap.createBitmap(width, height, sConfigs[config]); + } + + protected static boolean compressBitmap(Bitmap bitmap, int format, int quality, long ostreamPtr) { + NativeOStream stream = new NativeOStream(ostreamPtr); + return bitmap.compress(sFormats[format], quality, stream); + } + protected static String getCurrentThreadName() { return Thread.currentThread().getName(); } diff --git a/panda/src/android/config_android.cxx b/panda/src/android/config_android.cxx index 019443be4b..afbaf243ff 100644 --- a/panda/src/android/config_android.cxx +++ b/panda/src/android/config_android.cxx @@ -24,12 +24,24 @@ struct android_app *panda_android_app = NULL; jclass jni_PandaActivity; jmethodID jni_PandaActivity_readBitmapSize; jmethodID jni_PandaActivity_readBitmap; +jmethodID jni_PandaActivity_createBitmap; +jmethodID jni_PandaActivity_compressBitmap; jmethodID jni_PandaActivity_showToast; jclass jni_BitmapFactory_Options; jfieldID jni_BitmapFactory_Options_outWidth; jfieldID jni_BitmapFactory_Options_outHeight; +#ifndef HAVE_JPEG +static PNMFileTypeAndroid file_type_jpeg(PNMFileTypeAndroid::CF_jpeg); +#endif +#ifndef HAVE_PNG +static PNMFileTypeAndroid file_type_png(PNMFileTypeAndroid::CF_png); +#endif +#if __ANDROID_API__ >= 14 +static PNMFileTypeAndroid file_type_webp(PNMFileTypeAndroid::CF_webp); +#endif + /** * Initializes the library. This must be called at least once before any of * the functions or classes in this library can be used. Normally, this is @@ -37,10 +49,6 @@ jfieldID jni_BitmapFactory_Options_outHeight; */ void init_libandroid() { - PNMFileTypeRegistry *tr = PNMFileTypeRegistry::get_global_ptr(); - PNMFileTypeAndroid::init_type(); - PNMFileTypeAndroid::register_with_read_factory(); - tr->register_type(new PNMFileTypeAndroid); } /** @@ -48,7 +56,7 @@ init_libandroid() { * references and the method IDs. */ jint JNI_OnLoad(JavaVM *jvm, void *reserved) { - init_libandroid(); + //init_libandroid(); Thread *thread = Thread::get_current_thread(); JNIEnv *env = thread->get_jni_env(); @@ -63,6 +71,12 @@ jint JNI_OnLoad(JavaVM *jvm, void *reserved) { jni_PandaActivity_readBitmap = env->GetStaticMethodID(jni_PandaActivity, "readBitmap", "(JI)Landroid/graphics/Bitmap;"); + jni_PandaActivity_createBitmap = env->GetStaticMethodID(jni_PandaActivity, + "createBitmap", "(IIIZ)Landroid/graphics/Bitmap;"); + + jni_PandaActivity_compressBitmap = env->GetStaticMethodID(jni_PandaActivity, + "compressBitmap", "(Landroid/graphics/Bitmap;IIJ)Z"); + jni_PandaActivity_showToast = env->GetMethodID(jni_PandaActivity, "showToast", "(Ljava/lang/String;I)V"); @@ -72,6 +86,25 @@ jint JNI_OnLoad(JavaVM *jvm, void *reserved) { jni_BitmapFactory_Options_outWidth = env->GetFieldID(jni_BitmapFactory_Options, "outWidth", "I"); jni_BitmapFactory_Options_outHeight = env->GetFieldID(jni_BitmapFactory_Options, "outHeight", "I"); + nassertr(jni_PandaActivity_readBitmapSize, -1); + nassertr(jni_PandaActivity_readBitmap, -1); + nassertr(jni_PandaActivity_createBitmap, -1); + nassertr(jni_PandaActivity_compressBitmap, -1); + nassertr(jni_PandaActivity_showToast, -1); + + // We put this in JNI_OnLoad because it relies on Java classes, which + // are only available when launched from the Java VM. + PNMFileTypeRegistry *tr = PNMFileTypeRegistry::get_global_ptr(); +#ifndef HAVE_JPEG + tr->register_type(&file_type_jpeg); +#endif +#ifndef HAVE_PNG + tr->register_type(&file_type_png); +#endif +#if __ANDROID_API__ >= 14 + tr->register_type(&file_type_webp); +#endif + return JNI_VERSION_1_4; } @@ -86,6 +119,20 @@ void JNI_OnUnload(JavaVM *jvm, void *reserved) { env->DeleteGlobalRef(jni_PandaActivity); env->DeleteGlobalRef(jni_BitmapFactory_Options); + + // These will no longer work without JNI, so unregister them. + PNMFileTypeRegistry *tr = PNMFileTypeRegistry::get_global_ptr(); + if (tr != nullptr) { +#ifndef HAVE_JPEG + tr->unregister_type(&file_type_jpeg); +#endif +#ifndef HAVE_PNG + tr->unregister_type(&file_type_png); +#endif +#if __ANDROID_API__ >= 14 + tr->unregister_type(&file_type_webp); +#endif + } } /** diff --git a/panda/src/android/config_android.h b/panda/src/android/config_android.h index 74599ec3d9..2224dbd065 100644 --- a/panda/src/android/config_android.h +++ b/panda/src/android/config_android.h @@ -31,6 +31,8 @@ extern EXPORT_CLASS struct android_app* panda_android_app; extern jclass jni_PandaActivity; extern jmethodID jni_PandaActivity_readBitmapHeader; extern jmethodID jni_PandaActivity_readBitmap; +extern jmethodID jni_PandaActivity_createBitmap; +extern jmethodID jni_PandaActivity_compressBitmap; extern jmethodID jni_PandaActivity_showToast; extern jclass jni_BitmapFactory_Options; diff --git a/panda/src/android/jni_NativeOStream.cxx b/panda/src/android/jni_NativeOStream.cxx new file mode 100644 index 0000000000..c2779d0174 --- /dev/null +++ b/panda/src/android/jni_NativeOStream.cxx @@ -0,0 +1,54 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file jni_NativeOStream.cxx + * @author rdb + * @date 2018-02-10 + */ + +#include + +#include + +#if __GNUC__ >= 4 +#define EXPORT_JNI extern "C" __attribute__((visibility("default"))) +#else +#define EXPORT_JNI extern "C" +#endif + +/** + * Flushes the stream. + */ +EXPORT_JNI void +Java_org_panda3d_android_NativeOStream_nativeFlush(JNIEnv *env, jclass clazz, jlong ptr) { + std::ostream *stream = (std::ostream *)ptr; + + stream->flush(); +} + +/** + * Writes a single character to the ostream. + */ +EXPORT_JNI void +Java_org_panda3d_android_NativeOStream_nativePut(JNIEnv *env, jclass clazz, jlong ptr, int b) { + std::ostream *stream = (std::ostream *)ptr; + + stream->put((char)(b & 0xff)); +} + +/** + * Writes an array of bytes to the ostream. + */ +EXPORT_JNI void +Java_org_panda3d_android_NativeOStream_nativeWrite(JNIEnv *env, jclass clazz, jlong ptr, jbyteArray byte_array, jint offset, jint length) { + std::ostream *stream = (std::ostream *)ptr; + + jbyte *buffer = (jbyte *)alloca(length); + env->GetByteArrayRegion(byte_array, offset, length, buffer); + stream->write((char *)buffer, length); +} diff --git a/panda/src/android/p3android_composite1.cxx b/panda/src/android/p3android_composite1.cxx index 14b48ab99f..ea14d2b311 100644 --- a/panda/src/android/p3android_composite1.cxx +++ b/panda/src/android/p3android_composite1.cxx @@ -1,4 +1,6 @@ #include "config_android.cxx" #include "jni_NativeIStream.cxx" +#include "jni_NativeOStream.cxx" #include "pnmFileTypeAndroid.cxx" -#include "pnmFileTypeAndroidReader.cxx" \ No newline at end of file +#include "pnmFileTypeAndroidReader.cxx" +#include "pnmFileTypeAndroidWriter.cxx" diff --git a/panda/src/android/pnmFileTypeAndroid.cxx b/panda/src/android/pnmFileTypeAndroid.cxx index c661c6b3b0..aa25b2c777 100644 --- a/panda/src/android/pnmFileTypeAndroid.cxx +++ b/panda/src/android/pnmFileTypeAndroid.cxx @@ -17,24 +17,11 @@ #include "config_pnmimagetypes.h" -#include "pnmFileTypeRegistry.h" -#include "bamReader.h" - -static const char * const extensions_android[] = { - "jpg", "jpeg", "gif", "png", -#if __ANDROID_API__ >= 14 - "webp" -#endif -}; -static const int num_extensions_android = sizeof(extensions_android) / sizeof(const char *); - -TypeHandle PNMFileTypeAndroid::_type_handle; - /** * */ PNMFileTypeAndroid:: -PNMFileTypeAndroid() { +PNMFileTypeAndroid(CompressFormat format) : _format(format) { } /** @@ -51,7 +38,16 @@ get_name() const { */ int PNMFileTypeAndroid:: get_num_extensions() const { - return num_extensions_android; + switch (_format) { + case CF_jpeg: + return 3; + case CF_png: + return 1; + case CF_webp: + return 1; + default: + return 0; + } } /** @@ -60,8 +56,17 @@ get_num_extensions() const { */ string PNMFileTypeAndroid:: get_extension(int n) const { - nassertr(n >= 0 && n < num_extensions_android, string()); - return extensions_android[n]; + static const char *const jpeg_extensions[] = {"jpg", "jpeg", "jpe"}; + switch (_format) { + case CF_jpeg: + return jpeg_extensions[n]; + case CF_png: + return "png"; + case CF_webp: + return "webp"; + default: + return 0; + } } /** @@ -80,30 +85,17 @@ has_magic_number() const { */ PNMReader *PNMFileTypeAndroid:: make_reader(istream *file, bool owns_file, const string &magic_number) { - init_pnm(); return new Reader(this, file, owns_file, magic_number); } /** - * Registers the current object as something that can be read from a Bam file. + * Allocates and returns a new PNMWriter suitable for reading from this file + * type, if possible. If writing files of this type is not supported, returns + * NULL. */ -void PNMFileTypeAndroid:: -register_with_read_factory() { - BamReader::get_factory()-> - register_factory(get_class_type(), make_PNMFileTypeAndroid); -} - -/** - * This method is called by the BamReader when an object of this type is - * encountered in a Bam file; it should allocate and return a new object with - * all the data read. - * - * In the case of the PNMFileType objects, since these objects are all shared, - * we just pull the object from the registry. - */ -TypedWritable *PNMFileTypeAndroid:: -make_PNMFileTypeAndroid(const FactoryParams ¶ms) { - return PNMFileTypeRegistry::get_global_ptr()->get_type_by_handle(get_class_type()); +PNMWriter *PNMFileTypeAndroid:: +make_writer(ostream *file, bool owns_file) { + return new Writer(this, file, owns_file, _format); } #endif // ANDROID diff --git a/panda/src/android/pnmFileTypeAndroid.h b/panda/src/android/pnmFileTypeAndroid.h index cc49fd8f4f..f27cc4f23d 100644 --- a/panda/src/android/pnmFileTypeAndroid.h +++ b/panda/src/android/pnmFileTypeAndroid.h @@ -30,7 +30,13 @@ */ class EXPCL_PANDA_PNMIMAGETYPES PNMFileTypeAndroid : public PNMFileType { public: - PNMFileTypeAndroid(); + enum CompressFormat : jint { + CF_jpeg = 0, + CF_png = 1, + CF_webp = 2, + }; + + PNMFileTypeAndroid(CompressFormat format); virtual string get_name() const; @@ -41,6 +47,7 @@ public: virtual PNMReader *make_reader(istream *file, bool owns_file = true, const string &magic_number = string()); + virtual PNMWriter *make_writer(ostream *file, bool owns_file = true); public: class Reader : public PNMReader { @@ -60,29 +67,20 @@ public: int32_t _format; }; - // The TypedWritable interface follows. -public: - static void register_with_read_factory(); + class Writer : public PNMWriter { + public: + Writer(PNMFileType *type, ostream *file, bool owns_file, + CompressFormat format); -protected: - static TypedWritable *make_PNMFileTypeAndroid(const FactoryParams ¶ms); + virtual int write_data(xel *array, xelval *alpha); + virtual bool supports_grayscale() const; -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - PNMFileType::init_type(); - register_type(_type_handle, "PNMFileTypeAndroid", - PNMFileType::get_class_type()); - } - virtual TypeHandle get_type() const { - return get_class_type(); - } - virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + private: + CompressFormat _format; + }; private: - static TypeHandle _type_handle; + CompressFormat _format; }; #endif // ANDROID diff --git a/panda/src/android/pnmFileTypeAndroidWriter.cxx b/panda/src/android/pnmFileTypeAndroidWriter.cxx new file mode 100644 index 0000000000..4677a25cbd --- /dev/null +++ b/panda/src/android/pnmFileTypeAndroidWriter.cxx @@ -0,0 +1,146 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file pnmFileTypeAndroidWriter.cxx + * @author rdb + * @date 2018-02-10 + */ + +#include "pnmFileTypeAndroid.h" + +#ifdef ANDROID + +#include "config_pnmimagetypes.h" + +#include +#include + +// See android/graphics/Bitmap.java +enum class BitmapConfig : jint { + ALPHA_8 = 1, + RGB_565 = 3, + ARGB_4444 = 4, + ARGB_8888 = 5, + RGBA_F16 = 6, + HARDWARE = 7, +}; + +/** + * + */ +PNMFileTypeAndroid::Writer:: +Writer(PNMFileType *type, ostream *file, bool owns_file, + CompressFormat format) : + PNMWriter(type, file, owns_file), + _format(format) +{ +} + +/** + * Writes out an entire image all at once, including the header, based on the + * image data stored in the given _x_size * _y_size array and alpha pointers. + * (If the image type has no alpha channel, alpha is ignored.) Returns the + * number of rows correctly written. + * + * It is the user's responsibility to fill in the header data via calls to + * set_x_size(), set_num_channels(), etc., or copy_header_from(), before + * calling write_data(). + * + * It is important to delete the PNMWriter class after successfully writing + * the data. Failing to do this may result in some data not getting flushed! + * + * Derived classes need not override this if they instead provide + * supports_streaming() and write_row(), below. + */ +int PNMFileTypeAndroid::Writer:: +write_data(xel *array, xelval *alpha) { + size_t num_pixels = (size_t)_x_size * (size_t)_y_size; + + Thread *current_thread = Thread::get_current_thread(); + JNIEnv *env = current_thread->get_jni_env(); + nassertr(env != nullptr, 0); + + // Create a Bitmap object. + jobject bitmap = + env->CallStaticObjectMethod(jni_PandaActivity, + jni_PandaActivity_createBitmap, + (jint)_x_size, (jint)_y_size, + BitmapConfig::ARGB_8888, + (jboolean)has_alpha()); + nassertr(bitmap != nullptr, 0); + + // Get a writable pointer to write our pixel data to. + uint32_t *out; + int rc = AndroidBitmap_lockPixels(env, bitmap, (void **)&out); + if (rc != 0) { + android_cat.error() + << "Could not lock bitmap pixels (result code " << rc << ")\n"; + return 0; + } + + if (_maxval == 255) { + if (has_alpha() && alpha != nullptr) { + for (size_t i = 0; i < num_pixels; ++i) { + out[i] = (array[i].r) + | (array[i].g << 8u) + | (array[i].b << 16u) + | (alpha[i] << 24u); + } + } else { + for (size_t i = 0; i < num_pixels; ++i) { + out[i] = (array[i].r) + | (array[i].g << 8u) + | (array[i].b << 16u) + | 0xff000000u; + } + } + } else { + double ratio = 255.0 / _maxval; + if (has_alpha() && alpha != nullptr) { + for (size_t i = 0; i < num_pixels; ++i) { + out[i] = ((uint32_t)(array[i].r * ratio)) + | ((uint32_t)(array[i].g * ratio) << 8u) + | ((uint32_t)(array[i].b * ratio) << 16u) + | ((uint32_t)(alpha[i] * ratio) << 24u); + } + } else { + for (size_t i = 0; i < num_pixels; ++i) { + out[i] = ((uint32_t)(array[i].r * ratio)) + | ((uint32_t)(array[i].g * ratio) << 8u) + | ((uint32_t)(array[i].b * ratio) << 16u) + | 0xff000000u; + } + } + } + + // Finally, unlock the pixel data and compress it to the ostream. + AndroidBitmap_unlockPixels(env, bitmap); + jboolean res = + env->CallStaticBooleanMethod(jni_PandaActivity, + jni_PandaActivity_compressBitmap, + bitmap, _format, 85, (jlong)_file); + if (!res) { + android_cat.error() + << "Failed to compress bitmap.\n"; + return 0; + } + return _y_size; +} + +/** + * Returns true if this particular PNMWriter understands grayscale images. If + * this is false, then the rgb values of the xel array will be pre-filled with + * the same value across all three channels, to allow the writer to simply + * write out RGB data for a grayscale image. + */ +bool PNMFileTypeAndroid::Writer:: +supports_grayscale() const { + return false; +} + +#endif // ANDROID diff --git a/panda/src/android/pview_manifest.xml b/panda/src/android/pview_manifest.xml index 6b14579a80..30125c673f 100644 --- a/panda/src/android/pview_manifest.xml +++ b/panda/src/android/pview_manifest.xml @@ -6,6 +6,7 @@ android:versionName="1.0"> + diff --git a/panda/src/pnmimage/pnmFileTypeRegistry.cxx b/panda/src/pnmimage/pnmFileTypeRegistry.cxx index 261f311f11..96e24ef1bb 100644 --- a/panda/src/pnmimage/pnmFileTypeRegistry.cxx +++ b/panda/src/pnmimage/pnmFileTypeRegistry.cxx @@ -49,16 +49,19 @@ register_type(PNMFileType *type) { } // Make sure we haven't already registered this type. - Handles::iterator hi = _handles.find(type->get_type()); - if (hi != _handles.end()) { - pnmimage_cat->warning() - << "Attempt to register PNMFileType " << type->get_name() - << " (" << type->get_type() << ") more than once.\n"; - return; + TypeHandle handle = type->get_type(); + if (handle != PNMFileType::get_class_type()) { + Handles::iterator hi = _handles.find(handle); + if (hi != _handles.end()) { + pnmimage_cat->warning() + << "Attempt to register PNMFileType " << type->get_name() + << " (" << type->get_type() << ") more than once.\n"; + return; + } + _handles.insert(Handles::value_type(handle, type)); } _types.push_back(type); - _handles.insert(Handles::value_type(type->get_type(), type)); // Collect the unique extensions associated with the type. pset unique_extensions; @@ -82,6 +85,37 @@ register_type(PNMFileType *type) { _requires_sort = true; } +/** + * Removes a PNMFileType previously passed to register_type. + */ +void PNMFileTypeRegistry:: +unregister_type(PNMFileType *type) { + if (pnmimage_cat->is_debug()) { + pnmimage_cat->debug() + << "Unregistering image type " << type->get_name() << "\n"; + } + + TypeHandle handle = type->get_type(); + if (handle != PNMFileType::get_class_type()) { + Handles::iterator hi = _handles.find(handle); + if (hi != _handles.end()) { + _handles.erase(hi); + } + } + + _types.erase(std::remove(_types.begin(), _types.end(), type), + _types.end()); + + Extensions::iterator ei; + for (ei = _extensions.begin(); ei != _extensions.end(); ++ei) { + Types &types = ei->second; + types.erase(std::remove(types.begin(), types.end(), type), + types.end()); + } + + _requires_sort = true; +} + /** * Returns the total number of types registered. */ diff --git a/panda/src/pnmimage/pnmFileTypeRegistry.h b/panda/src/pnmimage/pnmFileTypeRegistry.h index aec9327d49..9c8702a961 100644 --- a/panda/src/pnmimage/pnmFileTypeRegistry.h +++ b/panda/src/pnmimage/pnmFileTypeRegistry.h @@ -33,6 +33,7 @@ public: ~PNMFileTypeRegistry(); void register_type(PNMFileType *type); + void unregister_type(PNMFileType *type); PUBLISHED: int get_num_types() const; From 8dea93bf3809c1688319952ea0aebfb2aa8cb2a0 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 17 Feb 2018 18:42:39 +0100 Subject: [PATCH 079/118] android: fix touch/stylus taps triggering mouse1 for compatibility --- panda/src/androiddisplay/androidGraphicsWindow.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/panda/src/androiddisplay/androidGraphicsWindow.cxx b/panda/src/androiddisplay/androidGraphicsWindow.cxx index 8a66cde45a..31cd4e23d6 100644 --- a/panda/src/androiddisplay/androidGraphicsWindow.cxx +++ b/panda/src/androiddisplay/androidGraphicsWindow.cxx @@ -545,6 +545,9 @@ handle_motion_event(const AInputEvent *event) { // The up event doesn't let us know which button is up, so we need to // keep track of the button state ourselves. int32_t button_state = AMotionEvent_getButtonState(event); + if (button_state == 0 && action == AMOTION_EVENT_ACTION_DOWN) { + button_state = AMOTION_EVENT_BUTTON_PRIMARY; + } int32_t changed = _mouse_button_state ^ button_state; if (changed != 0) { if (changed & AMOTION_EVENT_BUTTON_PRIMARY) { From 95eee0cab2ff912c68a9f368a3d7ac7480e30bf5 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 17 Feb 2018 20:07:58 +0100 Subject: [PATCH 080/118] android: load prc files from assets. Move assets to /android_asset --- makepanda/makepanda.py | 3 ++- panda/src/android/android_main.cxx | 35 +++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index ad974d38d0..3f75af73e4 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -7525,9 +7525,10 @@ def MakeInstallerAndroid(): continue copy_library(source, base) - # Copy the models as well. + # Copy the models and config files to the virtual assets filesystem. oscmd("mkdir apkroot/assets") oscmd("cp -R %s apkroot/assets/models" % (os.path.join(GetOutputDir(), "models"))) + oscmd("cp -R %s apkroot/assets/etc" % (os.path.join(GetOutputDir(), "etc"))) # Make an empty res folder. It's needed for the apk to be installable, apparently. oscmd("mkdir apkroot/res") diff --git a/panda/src/android/android_main.cxx b/panda/src/android/android_main.cxx index 05a98b768c..7d4e75e4b2 100644 --- a/panda/src/android/android_main.cxx +++ b/panda/src/android/android_main.cxx @@ -133,7 +133,8 @@ void android_main(struct android_app* app) { asset_mount = new VirtualFileMountAndroidAsset(app->activity->assetManager, apk_fn); VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - Filename asset_dir(apk_fn.get_dirname(), "assets"); + //Filename asset_dir(apk_fn.get_dirname(), "assets"); + Filename asset_dir("/android_asset"); vfs->mount(asset_mount, asset_dir, 0); // Release the apk_path. @@ -143,6 +144,33 @@ void android_main(struct android_app* app) { //TODO: prevent it from adding the directory multiple times. get_model_path().append_directory(asset_dir); + // Now load the configuration files. + vector pages; + ConfigPageManager *cp_mgr; + AAssetDir *etc = AAssetManager_openDir(app->activity->assetManager, "etc"); + if (etc != nullptr) { + cp_mgr = ConfigPageManager::get_global_ptr(); + const char *filename = AAssetDir_getNextFileName(etc); + while (filename != nullptr) { + // Does it match any of the configured prc patterns? + for (size_t i = 0; i < cp_mgr->get_num_prc_patterns(); ++i) { + GlobPattern pattern = cp_mgr->get_prc_pattern(i); + if (pattern.matches(filename)) { + Filename prc_fn("etc", filename); + istream *in = asset_mount->open_read_file(prc_fn); + if (in != nullptr) { + ConfigPage *page = cp_mgr->make_explicit_page(Filename("/android_asset", prc_fn)); + page->read_prc(*in); + pages.push_back(page); + } + break; + } + } + filename = AAssetDir_getNextFileName(etc); + } + AAssetDir_close(etc); + } + // Also read the intent filename. methodID = env->GetMethodID(activity_class, "getIntentDataPath", "()Ljava/lang/String;"); jstring filename = (jstring) env->CallObjectMethod(activity->clazz, methodID); @@ -229,6 +257,11 @@ void android_main(struct android_app* app) { android_cat.info() << "Destroy requested, exiting from android_main\n"; + for (ConfigPage *page : pages) { + cp_mgr->delete_explicit_page(page); + } + vfs->unmount(asset_mount); + if (filename_str != nullptr) { env->ReleaseStringUTFChars(filename, filename_str); } From 5c90f64182031d924179aab66cef9edb6f83cfe2 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 17 Feb 2018 20:48:58 +0100 Subject: [PATCH 081/118] text: fix is_whitespace() assertion when invalid chars are included --- panda/src/text/textAssembler.cxx | 7 ++++--- tests/text/test_text_assemble.py | 7 +++++++ 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 tests/text/test_text_assemble.py diff --git a/panda/src/text/textAssembler.cxx b/panda/src/text/textAssembler.cxx index a10179db04..49c507dafd 100644 --- a/panda/src/text/textAssembler.cxx +++ b/panda/src/text/textAssembler.cxx @@ -1626,10 +1626,11 @@ assemble_row(TextAssembler::TextRow &row, } if (first_glyph != (TextGlyph *)NULL) { - assert(!first_glyph->is_whitespace()); advance = first_glyph->get_advance() * advance_scale; - swap(placement._glyph, first_glyph); - placed_glyphs.push_back(placement); + if (!first_glyph->is_whitespace()) { + swap(placement._glyph, first_glyph); + placed_glyphs.push_back(placement); + } } // Check if there is a second glyph to create a hacky ligature or some diff --git a/tests/text/test_text_assemble.py b/tests/text/test_text_assemble.py new file mode 100644 index 0000000000..959097029c --- /dev/null +++ b/tests/text/test_text_assemble.py @@ -0,0 +1,7 @@ +from panda3d import core + +def test_text_assemble_null(): + # Tests that no is_whitespace() assert occurs + assembler = core.TextAssembler(core.TextEncoder()) + assembler.set_wtext(u"\0test") + assembler.assemble_text() From ad7669e12ace60a6b7470617c51054ea259d3ab3 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Fri, 16 Feb 2018 04:42:31 -0700 Subject: [PATCH 082/118] mathutil: Update FFTCompressor for FFTW3 This has been due for a while. The last FFTW 2.x release was in 1999. Note that this does change some of the loops; this has two benefits: 1) The halfcomplex storage order is now explained with a comment. 2) It fixed the special case "don't break a run of bytes for a zero" which was never triggering due to the value not being *exactly* 0.0. I have tested these changes against older FFT-compressed animation .bams and no noticeable decompression changes are present, so a .bam version bump is not necessary. --- makepanda/makepanda.py | 5 +- panda/src/mathutil/fftCompressor.cxx | 124 +++++++++++++++++++-------- 2 files changed, 91 insertions(+), 38 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 3f75af73e4..21c0181159 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -648,8 +648,7 @@ if (COMPILER == "MSVC"): if (PkgSkip("HARFBUZZ")==0): LibName("HARFBUZZ", GetThirdpartyDir() + "harfbuzz/lib/harfbuzz.lib") IncDirectory("HARFBUZZ", GetThirdpartyDir() + "harfbuzz/include/harfbuzz") - if (PkgSkip("FFTW")==0): LibName("FFTW", GetThirdpartyDir() + "fftw/lib/rfftw.lib") - if (PkgSkip("FFTW")==0): LibName("FFTW", GetThirdpartyDir() + "fftw/lib/fftw.lib") + if (PkgSkip("FFTW")==0): LibName("FFTW", GetThirdpartyDir() + "fftw/lib/fftw3.lib") if (PkgSkip("ARTOOLKIT")==0):LibName("ARTOOLKIT",GetThirdpartyDir() + "artoolkit/lib/libAR.lib") if (PkgSkip("OPENCV")==0): LibName("OPENCV", GetThirdpartyDir() + "opencv/lib/cv.lib") if (PkgSkip("OPENCV")==0): LibName("OPENCV", GetThirdpartyDir() + "opencv/lib/highgui.lib") @@ -816,7 +815,7 @@ if (COMPILER=="GCC"): SmartPkgEnable("FFMPEG", ffmpeg_libs, ffmpeg_libs, ("libavformat/avformat.h", "libavcodec/avcodec.h", "libavutil/avutil.h")) SmartPkgEnable("SWSCALE", "libswscale", "libswscale", ("libswscale/swscale.h"), target_pkg = "FFMPEG", thirdparty_dir = "ffmpeg") SmartPkgEnable("SWRESAMPLE","libswresample", "libswresample", ("libswresample/swresample.h"), target_pkg = "FFMPEG", thirdparty_dir = "ffmpeg") - SmartPkgEnable("FFTW", "", ("rfftw", "fftw"), ("fftw.h", "rfftw.h")) + SmartPkgEnable("FFTW", "", ("fftw3"), ("fftw.h")) SmartPkgEnable("FMODEX", "", ("fmodex"), ("fmodex", "fmodex/fmod.h")) SmartPkgEnable("FREETYPE", "freetype2", ("freetype"), ("freetype2", "freetype2/freetype/freetype.h")) SmartPkgEnable("HARFBUZZ", "harfbuzz", ("harfbuzz"), ("harfbuzz", "harfbuzz/hb-ft.h")) diff --git a/panda/src/mathutil/fftCompressor.cxx b/panda/src/mathutil/fftCompressor.cxx index f96c6f5f7c..0a5b7425e1 100644 --- a/panda/src/mathutil/fftCompressor.cxx +++ b/panda/src/mathutil/fftCompressor.cxx @@ -29,18 +29,14 @@ #undef howmany #endif -#ifdef PHAVE_DRFFTW_H - #include "drfftw.h" -#else - #include "rfftw.h" -#endif +#include "fftw3.h" // These FFTW support objects can only be defined if we actually have the FFTW // library available. -static rfftw_plan get_real_compress_plan(int length); -static rfftw_plan get_real_decompress_plan(int length); +static fftw_plan get_real_compress_plan(int length); +static fftw_plan get_real_decompress_plan(int length); -typedef pmap RealPlans; +typedef pmap RealPlans; static RealPlans _real_compress_plans; static RealPlans _real_decompress_plans; @@ -262,20 +258,31 @@ write_reals(Datagram &datagram, const PN_stdfloat *array, int length) { } // Now generate the Fourier transform. - double *data = (double *)alloca(length * sizeof(double)); + int fft_length = length / 2 + 1; + fftw_complex *fft_bins = (fftw_complex *)alloca(fft_length * sizeof(fftw_complex)); + + // This is for an in-place transform. It doesn't violate strict aliasing + // rules because &fft_bins[0][0] is still a double pointer. This saves on + // precious stack space. + double *data = &fft_bins[0][0]; int i; for (i = 0; i < length; i++) { data[i] = array[i]; } - double *half_complex = (double *)alloca(length * sizeof(double)); - - rfftw_plan plan = get_real_compress_plan(length); - rfftw_one(plan, data, half_complex); + // Note: This is an in-place DFT. `data` and `fft_bins` are aliases. + fftw_plan plan = get_real_compress_plan(length); + fftw_execute_dft_r2c(plan, data, fft_bins); // Now encode the numbers, run-length encoded by size, so we only write out // the number of bits we need for each number. + // Note that Panda3D has conventionally always used FFTW2's halfcomplex + // format for serializing the bins. In short, this means that for an n-length + // FFT, it stores: + // 1) The real components for bins 0 through floor(n/2), followed by... + // 2) The imaginary components for bins floor((n+1)/2)-1 through 1. + // (Imaginary component for bin 0 is never stored, as that's always zero.) vector_double run; RunWidth run_width = RW_invalid; @@ -286,8 +293,18 @@ write_reals(Datagram &datagram, const PN_stdfloat *array, int length) { static const double max_range_16 = 32767.0; static const double max_range_8 = 127.0; - double scale_factor = get_scale_factor(i, length); - double num = cfloor(half_complex[i] / scale_factor + 0.5); + int bin; // which FFT bin we're storing + int j; // 0=real; 1=imag + if (i < fft_length) { + bin = i; + j = 0; + } else { + bin = length - i; + j = 1; + } + + double scale_factor = get_scale_factor(bin, fft_length); + double num = cfloor(fft_bins[bin][j] / scale_factor + 0.5); // How many bits do we need to encode this integer? double a = fabs(num); @@ -313,16 +330,30 @@ write_reals(Datagram &datagram, const PN_stdfloat *array, int length) { // across a single intervening zero, don't interrupt the run just for // that. if (run_width == RW_8 && num_width == RW_0) { - if (i + 1 >= length || half_complex[i + 1] != 0.0) { + if (run.back() != 0) { num_width = RW_8; } } if (num_width != run_width) { // Now we need to flush the last run. + + // First, however, take care of the special case above: if we're + // switching from RW_8 to RW_0, there could be a zero at the end, which + // should be reclaimed into the RW_0 run. + bool reclaimed_zero = (run_width == RW_8 && num_width == RW_0 && + run.back() == 0); + if (reclaimed_zero) { + run.pop_back(); + } + num_written += write_run(datagram, run_width, run); run.clear(); run_width = num_width; + + if (reclaimed_zero) { + run.push_back(0); + } } run.push_back(num); @@ -595,14 +626,36 @@ read_reals(DatagramIterator &di, vector_stdfloat &array) { nassertr(num_read == length, false); nassertr((int)half_complex.size() == length, false); + int fft_length = length / 2 + 1; + fftw_complex *fft_bins = (fftw_complex *)alloca(fft_length * sizeof(fftw_complex)); + int i; - for (i = 0; i < length; i++) { - half_complex[i] *= get_scale_factor(i, length); + for (i = 0; i < fft_length; i++) { + double scale_factor = get_scale_factor(i, fft_length); + + // For an explanation of this, see the compression code's comment about the + // halfcomplex format. + + fft_bins[i][0] = half_complex[i] * scale_factor; + if (i == 0) { + // First bin doesn't store imaginary component + fft_bins[i][1] = 0.0; + } else if ((i == fft_length - 1) && !(length & 1)) { + // Last bin doesn't store imaginary component with even lengths + fft_bins[i][1] = 0.0; + } else { + fft_bins[i][1] = half_complex[length - i] * scale_factor; + } } - double *data = (double *)alloca(length * sizeof(double)); - rfftw_plan plan = get_real_decompress_plan(length); - rfftw_one(plan, &half_complex[0], data); + // This is for an in-place transform. It doesn't violate strict aliasing + // rules because &fft_bins[0][0] is still a double pointer. This saves on + // precious stack space. + double *data = &fft_bins[0][0]; + + // Note: This is an in-place DFT. `data` and `fft_bins` are aliases. + fftw_plan plan = get_real_decompress_plan(length); + fftw_execute_dft_c2r(plan, fft_bins, data); double scale = 1.0 / (double)length; array.reserve(array.size() + length); @@ -770,14 +823,14 @@ free_storage() { for (pi = _real_compress_plans.begin(); pi != _real_compress_plans.end(); ++pi) { - rfftw_destroy_plan((*pi).second); + fftw_destroy_plan((*pi).second); } _real_compress_plans.clear(); for (pi = _real_decompress_plans.begin(); pi != _real_decompress_plans.end(); ++pi) { - rfftw_destroy_plan((*pi).second); + fftw_destroy_plan((*pi).second); } _real_decompress_plans.clear(); #endif @@ -933,17 +986,18 @@ read_run(DatagramIterator &di, vector_double &run) { } /** - * Returns the appropriate scaling for the given position within the - * halfcomplex array. + * Returns the appropriate scaling for the given bin in the FFT output. + * + * The scale factor is the value of one integer in the quantized data. As such, + * greater bins (higher, more noticeable frequencies) have *lower* scaling + * factors, which means greater precision. */ double FFTCompressor:: get_scale_factor(int i, int length) const { - int m = (length / 2) + 1; - int k = (i < m) ? i : length - i; - nassertr(k >= 0 && k < m, 1.0); + nassertr(i < length, 1.0); return _fft_offset + - _fft_factor * pow((double)(m-1 - k) / (double)(m-1), _fft_exponent); + _fft_factor * pow((double)(length - i) / (double)(length), _fft_exponent); } /** @@ -997,7 +1051,7 @@ get_compressability(const PN_stdfloat *data, int length) const { * Returns a FFTW plan suitable for compressing a float array of the indicated * length. */ -static rfftw_plan +static fftw_plan get_real_compress_plan(int length) { RealPlans::iterator pi; pi = _real_compress_plans.find(length); @@ -1005,8 +1059,8 @@ get_real_compress_plan(int length) { return (*pi).second; } - rfftw_plan plan; - plan = rfftw_create_plan(length, FFTW_REAL_TO_COMPLEX, FFTW_ESTIMATE); + fftw_plan plan; + plan = fftw_plan_dft_r2c_1d(length, NULL, NULL, FFTW_ESTIMATE); _real_compress_plans.insert(RealPlans::value_type(length, plan)); return plan; @@ -1016,7 +1070,7 @@ get_real_compress_plan(int length) { * Returns a FFTW plan suitable for decompressing a float array of the * indicated length. */ -static rfftw_plan +static fftw_plan get_real_decompress_plan(int length) { RealPlans::iterator pi; pi = _real_decompress_plans.find(length); @@ -1024,8 +1078,8 @@ get_real_decompress_plan(int length) { return (*pi).second; } - rfftw_plan plan; - plan = rfftw_create_plan(length, FFTW_COMPLEX_TO_REAL, FFTW_ESTIMATE); + fftw_plan plan; + plan = fftw_plan_dft_c2r_1d(length, NULL, NULL, FFTW_ESTIMATE); _real_decompress_plans.insert(RealPlans::value_type(length, plan)); return plan; From 68b1ecfd32408bfef4ad7d5124f1a6775f8d7b64 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Sat, 17 Feb 2018 23:00:26 -0700 Subject: [PATCH 083/118] parser-inc: Add stub for FFTW3 header --- dtool/src/parser-inc/{rfftw.h => fftw3.h} | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) rename dtool/src/parser-inc/{rfftw.h => fftw3.h} (75%) diff --git a/dtool/src/parser-inc/rfftw.h b/dtool/src/parser-inc/fftw3.h similarity index 75% rename from dtool/src/parser-inc/rfftw.h rename to dtool/src/parser-inc/fftw3.h index 47bb2102d1..c5b3b8131d 100644 --- a/dtool/src/parser-inc/rfftw.h +++ b/dtool/src/parser-inc/fftw3.h @@ -6,11 +6,9 @@ * license. You should have received a copy of this license along * with this source code in a file named "LICENSE." * - * @file rfftw.h - * @author drose - * @date 2007-06-27 + * @file fftw.h + * @author cfsworks + * @date 2018-02-17 */ -typedef struct _rfftw_plan rfftw_plan; - - +typedef struct _fftw_plan fftw_plan; From bc88566906f80b49fc93cdbdf107189d3d96c606 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Mon, 19 Feb 2018 19:15:02 -0700 Subject: [PATCH 084/118] tests: Add xfail test for loading a missing audio file --- tests/audio/conftest.py | 6 ++++++ tests/audio/test_loading.py | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 tests/audio/conftest.py create mode 100644 tests/audio/test_loading.py diff --git a/tests/audio/conftest.py b/tests/audio/conftest.py new file mode 100644 index 0000000000..60636e6d6e --- /dev/null +++ b/tests/audio/conftest.py @@ -0,0 +1,6 @@ +import pytest +from panda3d.core import * + +@pytest.fixture(scope='module') +def audiomgr(): + return AudioManager.create_AudioManager() diff --git a/tests/audio/test_loading.py b/tests/audio/test_loading.py new file mode 100644 index 0000000000..ddbec1a1da --- /dev/null +++ b/tests/audio/test_loading.py @@ -0,0 +1,6 @@ +import pytest + +@pytest.mark.xfail +def test_missing_file(audiomgr): + sound = audiomgr.get_sound('/not/a/valid/file.ogg') + assert sound is None From f970bc32292dd669fdc1427aaa4f3f36cd10fbce Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Mon, 19 Feb 2018 21:27:28 -0700 Subject: [PATCH 085/118] openal: Don't return OpenALAudioSounds that fail to initialize Also don't register them in _all_sounds, where they won't remove themselves due to having already called cleanup() on themselves. Additionally stops a sound in a cleaned-up state from being passed to the app and played. --- panda/src/audiotraits/openalAudioManager.cxx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/panda/src/audiotraits/openalAudioManager.cxx b/panda/src/audiotraits/openalAudioManager.cxx index 8d38eeec49..cbdaadbfa1 100644 --- a/panda/src/audiotraits/openalAudioManager.cxx +++ b/panda/src/audiotraits/openalAudioManager.cxx @@ -471,6 +471,12 @@ get_sound(MovieAudio *sound, bool positional, int mode) { PT(OpenALAudioSound) oas = new OpenALAudioSound(this, sound, positional, mode); + if(!oas->_manager) { + // The sound cleaned itself up immediately. It pretty clearly didn't like + // something, so we should just return a null sound instead. + return get_null_sound(); + } + _all_sounds.insert(oas); PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas; return res; @@ -500,6 +506,12 @@ get_sound(const string &file_name, bool positional, int mode) { PT(OpenALAudioSound) oas = new OpenALAudioSound(this, mva, positional, mode); + if(!oas->_manager) { + // The sound cleaned itself up immediately. It pretty clearly didn't like + // something, so we should just return a null sound instead. + return get_null_sound(); + } + _all_sounds.insert(oas); PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas; return res; From 50b3b87ad51ddba70405b3acc8ae2addf477fff6 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Mon, 19 Feb 2018 23:50:05 -0700 Subject: [PATCH 086/118] openal: Explicitly signal a needed cleanup from require_sound_data --- panda/src/audiotraits/openalAudioSound.I | 7 +++++-- panda/src/audiotraits/openalAudioSound.cxx | 12 +++++++----- panda/src/audiotraits/openalAudioSound.h | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/panda/src/audiotraits/openalAudioSound.I b/panda/src/audiotraits/openalAudioSound.I index a68d36ee12..1b7e56a82c 100644 --- a/panda/src/audiotraits/openalAudioSound.I +++ b/panda/src/audiotraits/openalAudioSound.I @@ -37,16 +37,19 @@ get_calibrated_clock(double rtc) const { /** * Makes sure the sound data record is present, and if not, obtains it. + * + * Returns true on success, false on failure. */ -void OpenALAudioSound:: +bool OpenALAudioSound:: require_sound_data() { if (_sd==0) { _sd = _manager->get_sound_data(_movie, _desired_mode); if (_sd==0) { audio_error("Could not open audio " << _movie->get_filename()); - cleanup(); + return false; } } + return true; } /** diff --git a/panda/src/audiotraits/openalAudioSound.cxx b/panda/src/audiotraits/openalAudioSound.cxx index 9abaf24036..d3c5ff7291 100644 --- a/panda/src/audiotraits/openalAudioSound.cxx +++ b/panda/src/audiotraits/openalAudioSound.cxx @@ -69,8 +69,8 @@ OpenALAudioSound(OpenALAudioManager* manager, ReMutexHolder holder(OpenALAudioManager::_lock); - require_sound_data(); - if (_manager == NULL) { + if (!require_sound_data()) { + cleanup(); return; } @@ -130,10 +130,12 @@ play() { stop(); - require_sound_data(); - if (_manager == 0) return; - _manager->starting_sound(this); + if (!require_sound_data()) { + cleanup(); + return; + } + _manager->starting_sound(this); if (!_source) { return; } diff --git a/panda/src/audiotraits/openalAudioSound.h b/panda/src/audiotraits/openalAudioSound.h index 3442acb4f2..f5b02667e5 100644 --- a/panda/src/audiotraits/openalAudioSound.h +++ b/panda/src/audiotraits/openalAudioSound.h @@ -116,7 +116,7 @@ private: int read_stream_data(int bytelen, unsigned char *data); void pull_used_buffers(); void push_fresh_buffers(); - INLINE void require_sound_data(); + INLINE bool require_sound_data(); INLINE void release_sound_data(); private: From 7aedc2151035174632a7f3e55be7563f71e65117 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Tue, 20 Feb 2018 00:55:13 -0700 Subject: [PATCH 087/118] tests: Update audio test to recognize missing sounds as NullAudioSound --- tests/audio/test_loading.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/audio/test_loading.py b/tests/audio/test_loading.py index ddbec1a1da..ea3bdb2cd9 100644 --- a/tests/audio/test_loading.py +++ b/tests/audio/test_loading.py @@ -1,6 +1,5 @@ import pytest -@pytest.mark.xfail def test_missing_file(audiomgr): sound = audiomgr.get_sound('/not/a/valid/file.ogg') - assert sound is None + assert str(sound).startswith('NullAudioSound') From 9afdb78d947e51ce84e09a4885ff3c3895f8bc4e Mon Sep 17 00:00:00 2001 From: deflected Date: Wed, 21 Feb 2018 15:04:47 +0100 Subject: [PATCH 088/118] bullet: make thread-safe by adding global lock mechanism Also addresses some memory leaks. --- .../bulletBaseCharacterControllerNode.h | 4 +- panda/src/bullet/bulletBodyNode.I | 129 ----- panda/src/bullet/bulletBodyNode.cxx | 240 ++++++++- panda/src/bullet/bulletBodyNode.h | 31 +- panda/src/bullet/bulletBoxShape.I | 17 - panda/src/bullet/bulletBoxShape.cxx | 24 + panda/src/bullet/bulletBoxShape.h | 4 +- panda/src/bullet/bulletCapsuleShape.I | 20 - panda/src/bullet/bulletCapsuleShape.cxx | 27 + panda/src/bullet/bulletCapsuleShape.h | 4 +- .../bullet/bulletCharacterControllerNode.I | 2 + .../bullet/bulletCharacterControllerNode.cxx | 46 +- .../bullet/bulletCharacterControllerNode.h | 6 +- panda/src/bullet/bulletConeShape.I | 20 - panda/src/bullet/bulletConeShape.cxx | 26 + panda/src/bullet/bulletConeShape.h | 4 +- panda/src/bullet/bulletConeTwistConstraint.I | 18 - .../src/bullet/bulletConeTwistConstraint.cxx | 31 ++ panda/src/bullet/bulletConeTwistConstraint.h | 4 +- panda/src/bullet/bulletConvexHullShape.I | 16 - panda/src/bullet/bulletConvexHullShape.cxx | 26 + panda/src/bullet/bulletConvexHullShape.h | 4 +- .../src/bullet/bulletConvexPointCloudShape.I | 27 - .../bullet/bulletConvexPointCloudShape.cxx | 32 ++ .../src/bullet/bulletConvexPointCloudShape.h | 6 +- panda/src/bullet/bulletCylinderShape.I | 44 -- panda/src/bullet/bulletCylinderShape.cxx | 55 ++ panda/src/bullet/bulletCylinderShape.h | 10 +- panda/src/bullet/bulletDebugNode.cxx | 5 +- panda/src/bullet/bulletDebugNode.h | 4 +- panda/src/bullet/bulletGenericConstraint.I | 18 - panda/src/bullet/bulletGenericConstraint.cxx | 28 + panda/src/bullet/bulletGenericConstraint.h | 4 +- panda/src/bullet/bulletGhostNode.I | 20 - panda/src/bullet/bulletGhostNode.cxx | 52 +- panda/src/bullet/bulletGhostNode.h | 10 +- panda/src/bullet/bulletHeightfieldShape.I | 30 -- panda/src/bullet/bulletHeightfieldShape.cxx | 40 +- panda/src/bullet/bulletHeightfieldShape.h | 4 +- panda/src/bullet/bulletHingeConstraint.I | 18 - panda/src/bullet/bulletHingeConstraint.cxx | 33 ++ panda/src/bullet/bulletHingeConstraint.h | 4 +- panda/src/bullet/bulletManifoldPoint.I | 225 -------- panda/src/bullet/bulletManifoldPoint.cxx | 266 ++++++++++ panda/src/bullet/bulletManifoldPoint.h | 46 +- panda/src/bullet/bulletMinkowskiSumShape.I | 58 --- panda/src/bullet/bulletMinkowskiSumShape.cxx | 66 +++ panda/src/bullet/bulletMinkowskiSumShape.h | 12 +- panda/src/bullet/bulletMultiSphereShape.I | 45 -- panda/src/bullet/bulletMultiSphereShape.cxx | 52 ++ panda/src/bullet/bulletMultiSphereShape.h | 10 +- panda/src/bullet/bulletPersistentManifold.cxx | 7 + panda/src/bullet/bulletPlaneShape.I | 34 -- panda/src/bullet/bulletPlaneShape.cxx | 40 ++ panda/src/bullet/bulletPlaneShape.h | 8 +- panda/src/bullet/bulletRigidBodyNode.I | 35 -- panda/src/bullet/bulletRigidBodyNode.cxx | 146 +++++- panda/src/bullet/bulletRigidBodyNode.h | 18 +- panda/src/bullet/bulletRotationalLimitMotor.I | 159 +----- .../src/bullet/bulletRotationalLimitMotor.cxx | 176 ++++++- panda/src/bullet/bulletRotationalLimitMotor.h | 38 +- panda/src/bullet/bulletShape.I | 63 --- panda/src/bullet/bulletShape.cxx | 89 +++- panda/src/bullet/bulletShape.h | 15 +- panda/src/bullet/bulletSliderConstraint.I | 18 - panda/src/bullet/bulletSliderConstraint.cxx | 43 ++ panda/src/bullet/bulletSliderConstraint.h | 4 +- panda/src/bullet/bulletSoftBodyConfig.I | 436 ---------------- panda/src/bullet/bulletSoftBodyConfig.cxx | 489 ++++++++++++++++++ panda/src/bullet/bulletSoftBodyConfig.h | 96 ++-- panda/src/bullet/bulletSoftBodyMaterial.I | 63 --- panda/src/bullet/bulletSoftBodyMaterial.cxx | 69 +++ panda/src/bullet/bulletSoftBodyMaterial.h | 14 +- panda/src/bullet/bulletSoftBodyNode.I | 53 -- panda/src/bullet/bulletSoftBodyNode.cxx | 148 +++++- panda/src/bullet/bulletSoftBodyNode.h | 19 +- panda/src/bullet/bulletSoftBodyShape.cxx | 1 + panda/src/bullet/bulletSoftBodyWorldInfo.cxx | 11 + panda/src/bullet/bulletSphereShape.I | 18 - panda/src/bullet/bulletSphereShape.cxx | 23 + panda/src/bullet/bulletSphereShape.h | 4 +- .../src/bullet/bulletSphericalConstraint.cxx | 4 + .../bullet/bulletTranslationalLimitMotor.I | 161 +----- .../bullet/bulletTranslationalLimitMotor.cxx | 179 ++++++- .../bullet/bulletTranslationalLimitMotor.h | 36 +- panda/src/bullet/bulletTriangleMesh.I | 28 - panda/src/bullet/bulletTriangleMesh.cxx | 81 ++- panda/src/bullet/bulletTriangleMesh.h | 12 +- panda/src/bullet/bulletTriangleMeshShape.I | 21 - panda/src/bullet/bulletTriangleMeshShape.cxx | 28 +- panda/src/bullet/bulletTriangleMeshShape.h | 4 +- panda/src/bullet/bulletVehicle.I | 117 +---- panda/src/bullet/bulletVehicle.cxx | 173 ++++++- panda/src/bullet/bulletVehicle.h | 29 +- panda/src/bullet/bulletWheel.I | 71 --- panda/src/bullet/bulletWheel.cxx | 128 +++++ panda/src/bullet/bulletWheel.h | 16 +- panda/src/bullet/bulletWorld.I | 135 ----- panda/src/bullet/bulletWorld.cxx | 436 +++++++++++++--- panda/src/bullet/bulletWorld.h | 65 ++- 100 files changed, 3469 insertions(+), 2519 deletions(-) diff --git a/panda/src/bullet/bulletBaseCharacterControllerNode.h b/panda/src/bullet/bulletBaseCharacterControllerNode.h index e7abe41886..eafc561c67 100644 --- a/panda/src/bullet/bulletBaseCharacterControllerNode.h +++ b/panda/src/bullet/bulletBaseCharacterControllerNode.h @@ -43,8 +43,8 @@ public: virtual btPairCachingGhostObject *get_ghost() const = 0; virtual btCharacterControllerInterface *get_character() const = 0; - virtual void sync_p2b(PN_stdfloat dt, int num_substeps) = 0; - virtual void sync_b2p() = 0; + virtual void do_sync_p2b(PN_stdfloat dt, int num_substeps) = 0; + virtual void do_sync_b2p() = 0; public: static TypeHandle get_class_type() { diff --git a/panda/src/bullet/bulletBodyNode.I b/panda/src/bullet/bulletBodyNode.I index 01e62e8bd6..6f9aac9f45 100644 --- a/panda/src/bullet/bulletBodyNode.I +++ b/panda/src/bullet/bulletBodyNode.I @@ -84,51 +84,6 @@ get_collision_response() const { return !get_collision_flag(btCollisionObject::CF_NO_CONTACT_RESPONSE); } -/** - * - */ -INLINE void BulletBodyNode:: -set_collision_flag(int flag, bool value) { - - int flags = get_object()->getCollisionFlags(); - - if (value == true) { - flags |= flag; - } - else { - flags &= ~(flag); - } - - get_object()->setCollisionFlags(flags); -} - -/** - * - */ -INLINE bool BulletBodyNode:: -get_collision_flag(int flag) const { - - return (get_object()->getCollisionFlags() & flag) ? true : false; -} - -/** - * - */ -INLINE bool BulletBodyNode:: -is_static() const { - - return get_object()->isStaticObject(); -} - -/** - * - */ -INLINE bool BulletBodyNode:: -is_kinematic() const { - - return get_object()->isKinematicObject(); -} - /** * */ @@ -147,90 +102,6 @@ set_kinematic(bool value) { set_collision_flag(btCollisionObject::CF_KINEMATIC_OBJECT, value); } -/** - * - */ -INLINE PN_stdfloat BulletBodyNode:: -get_restitution() const { - - return get_object()->getRestitution(); -} - -/** - * - */ -INLINE void BulletBodyNode:: -set_restitution(PN_stdfloat restitution) { - - return get_object()->setRestitution(restitution); -} - -/** - * - */ -INLINE PN_stdfloat BulletBodyNode:: -get_friction() const { - - return get_object()->getFriction(); -} - -/** - * - */ -INLINE void BulletBodyNode:: -set_friction(PN_stdfloat friction) { - - return get_object()->setFriction(friction); -} - -#if BT_BULLET_VERSION >= 281 -/** - * - */ -INLINE PN_stdfloat BulletBodyNode:: -get_rolling_friction() const { - - return get_object()->getRollingFriction(); -} - -/** - * - */ -INLINE void BulletBodyNode:: -set_rolling_friction(PN_stdfloat friction) { - - return get_object()->setRollingFriction(friction); -} -#endif - -/** - * - */ -INLINE bool BulletBodyNode:: -has_anisotropic_friction() const { - - return get_object()->hasAnisotropicFriction(); -} - -/** - * - */ -INLINE int BulletBodyNode:: -get_num_shapes() const { - - return _shapes.size(); -} - -/** - * - */ -INLINE BulletShape *BulletBodyNode:: -get_shape(int idx) const { - - nassertr(idx >= 0 && idx < (int)_shapes.size(), NULL); - return _shapes[idx]; -} - /** * Enables or disables the debug visualisation for this collision object. By * default the debug visualisation is enabled. diff --git a/panda/src/bullet/bulletBodyNode.cxx b/panda/src/bullet/bulletBodyNode.cxx index b078864ccc..f02641cd96 100644 --- a/panda/src/bullet/bulletBodyNode.cxx +++ b/panda/src/bullet/bulletBodyNode.cxx @@ -41,9 +41,11 @@ BulletBodyNode(const char *name) : PandaNode(name) { */ BulletBodyNode:: BulletBodyNode(const BulletBodyNode ©) : - PandaNode(copy), - _shapes(copy._shapes) + PandaNode(copy) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shapes = copy._shapes; if (copy._shape && copy._shape->getShapeType() == COMPOUND_SHAPE_PROXYTYPE) { // btCompoundShape does not define a copy constructor. Manually copy. btCompoundShape *shape = new btCompoundShape; @@ -148,16 +150,168 @@ safe_to_flatten_below() const { * */ void BulletBodyNode:: -output(ostream &out) const { +do_output(ostream &out) const { PandaNode::output(out); - out << " (" << get_num_shapes() << " shapes)"; + out << " (" << _shapes.size() << " shapes)"; - out << (is_active() ? " active" : " inactive"); + out << (get_object()->isActive() ? " active" : " inactive"); - if (is_static()) out << " static"; - if (is_kinematic()) out << " kinematic"; + if (get_object()->isStaticObject()) out << " static"; + if (get_object()->isKinematicObject()) out << " kinematic"; +} + +/** + * + */ +void BulletBodyNode:: +output(ostream &out) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + do_output(out); +} + +/** + * + */ +void BulletBodyNode:: +set_collision_flag(int flag, bool value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + int flags = get_object()->getCollisionFlags(); + + if (value == true) { + flags |= flag; + } + else { + flags &= ~(flag); + } + + get_object()->setCollisionFlags(flags); +} + +/** + * + */ +bool BulletBodyNode:: +get_collision_flag(int flag) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (get_object()->getCollisionFlags() & flag) ? true : false; +} + +/** + * + */ +bool BulletBodyNode:: +is_static() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->isStaticObject(); +} + +/** + * + */ +bool BulletBodyNode:: +is_kinematic() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->isKinematicObject(); +} + +/** + * + */ +PN_stdfloat BulletBodyNode:: +get_restitution() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->getRestitution(); +} + +/** + * + */ +void BulletBodyNode:: +set_restitution(PN_stdfloat restitution) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->setRestitution(restitution); +} + +/** + * + */ +PN_stdfloat BulletBodyNode:: +get_friction() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->getFriction(); +} + +/** + * + */ +void BulletBodyNode:: +set_friction(PN_stdfloat friction) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->setFriction(friction); +} + +#if BT_BULLET_VERSION >= 281 +/** + * + */ +PN_stdfloat BulletBodyNode:: +get_rolling_friction() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->getRollingFriction(); +} + +/** + * + */ +void BulletBodyNode:: +set_rolling_friction(PN_stdfloat friction) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->setRollingFriction(friction); +} +#endif + +/** + * + */ +bool BulletBodyNode:: +has_anisotropic_friction() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return get_object()->hasAnisotropicFriction(); +} + +/** + * + */ +int BulletBodyNode:: +get_num_shapes() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _shapes.size(); +} + +/** + * + */ +BulletShape *BulletBodyNode:: +get_shape(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertr(idx >= 0 && idx < (int)_shapes.size(), NULL); + return _shapes[idx]; } /** @@ -165,6 +319,16 @@ output(ostream &out) const { */ void BulletBodyNode:: add_shape(BulletShape *bullet_shape, const TransformState *ts) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + do_add_shape(bullet_shape, ts); +} + +/** + * Assumes the lock(bullet global lock) is held by the caller + */ +void BulletBodyNode:: +do_add_shape(BulletShape *bullet_shape, const TransformState *ts) { nassertv(get_object()); nassertv(ts); @@ -246,7 +410,7 @@ add_shape(BulletShape *bullet_shape, const TransformState *ts) { // Restore the local scaling again np.set_scale(scale); - shape_changed(); + do_shape_changed(); } /** @@ -254,6 +418,7 @@ add_shape(BulletShape *bullet_shape, const TransformState *ts) { */ void BulletBodyNode:: remove_shape(BulletShape *shape) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(get_object()); @@ -312,7 +477,7 @@ remove_shape(BulletShape *shape) { compound->removeChildShape(shape->ptr()); } - shape_changed(); + do_shape_changed(); } } @@ -333,6 +498,7 @@ is_identity(btTransform &trans) { */ LPoint3 BulletBodyNode:: get_shape_pos(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertr(idx >= 0 && idx < (int)_shapes.size(), LPoint3::zero()); @@ -352,14 +518,17 @@ get_shape_pos(int idx) const { */ LMatrix4 BulletBodyNode:: get_shape_mat(int idx) const { - return get_shape_transform(idx)->get_mat(); + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return do_get_shape_transform(idx)->get_mat(); } /** * */ CPT(TransformState) BulletBodyNode:: -get_shape_transform(int idx) const { +do_get_shape_transform(int idx) const { + nassertr(idx >= 0 && idx < (int)_shapes.size(), TransformState::make_identity()); btCollisionShape *root = get_object()->getCollisionShape(); @@ -386,13 +555,25 @@ get_shape_transform(int idx) const { return TransformState::make_identity(); } +/** + * + */ +CPT(TransformState) BulletBodyNode:: +get_shape_transform(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return do_get_shape_transform(idx); +} + + /** * Hook which will be called whenever the total shape of a body changed. Used * for example to update the mass properties (inertia) of a rigid body. The * default implementation does nothing. + * Assumes the lock(bullet global lock) is held */ void BulletBodyNode:: -shape_changed() { +do_shape_changed() { } @@ -401,6 +582,7 @@ shape_changed() { */ void BulletBodyNode:: set_deactivation_time(PN_stdfloat dt) { + LightMutexHolder holder(BulletWorld::get_global_lock()); get_object()->setDeactivationTime(dt); } @@ -410,6 +592,7 @@ set_deactivation_time(PN_stdfloat dt) { */ PN_stdfloat BulletBodyNode:: get_deactivation_time() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return get_object()->getDeactivationTime(); } @@ -419,6 +602,7 @@ get_deactivation_time() const { */ bool BulletBodyNode:: is_active() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return get_object()->isActive(); } @@ -428,6 +612,7 @@ is_active() const { */ void BulletBodyNode:: set_active(bool active, bool force) { + LightMutexHolder holder(BulletWorld::get_global_lock()); if (active) { get_object()->activate(force); @@ -457,10 +642,12 @@ force_active(bool active) { */ void BulletBodyNode:: set_deactivation_enabled(bool enabled) { + LightMutexHolder holder(BulletWorld::get_global_lock()); // Don't change the state if it's currently active and we enable // deactivation. - if (enabled != is_deactivation_enabled()) { + bool is_enabled = get_object()->getActivationState() != DISABLE_DEACTIVATION; + if (enabled != is_enabled) { // It's OK to set to ACTIVE_TAG even if we don't mean to activate it; it // will be disabled right away if the deactivation timer has run out. @@ -474,6 +661,7 @@ set_deactivation_enabled(bool enabled) { */ bool BulletBodyNode:: is_deactivation_enabled() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (get_object()->getActivationState() != DISABLE_DEACTIVATION); } @@ -483,6 +671,7 @@ is_deactivation_enabled() const { */ bool BulletBodyNode:: check_collision_with(PandaNode *node) { + LightMutexHolder holder(BulletWorld::get_global_lock()); btCollisionObject *obj = BulletWorld::get_collision_object(node); @@ -499,6 +688,7 @@ check_collision_with(PandaNode *node) { */ LVecBase3 BulletBodyNode:: get_anisotropic_friction() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVecBase3(get_object()->getAnisotropicFriction()); } @@ -508,6 +698,7 @@ get_anisotropic_friction() const { */ void BulletBodyNode:: set_anisotropic_friction(const LVecBase3 &friction) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!friction.is_nan()); get_object()->setAnisotropicFriction(LVecBase3_to_btVector3(friction)); @@ -518,6 +709,7 @@ set_anisotropic_friction(const LVecBase3 &friction) { */ bool BulletBodyNode:: has_contact_response() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return get_object()->hasContactResponse(); } @@ -527,7 +719,8 @@ has_contact_response() const { */ PN_stdfloat BulletBodyNode:: get_contact_processing_threshold() const { - + LightMutexHolder holder(BulletWorld::get_global_lock()); + return get_object()->getContactProcessingThreshold(); } @@ -537,6 +730,7 @@ get_contact_processing_threshold() const { */ void BulletBodyNode:: set_contact_processing_threshold(PN_stdfloat threshold) { + LightMutexHolder holder(BulletWorld::get_global_lock()); get_object()->setContactProcessingThreshold(threshold); } @@ -546,6 +740,7 @@ set_contact_processing_threshold(PN_stdfloat threshold) { */ PN_stdfloat BulletBodyNode:: get_ccd_swept_sphere_radius() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return get_object()->getCcdSweptSphereRadius(); } @@ -555,6 +750,7 @@ get_ccd_swept_sphere_radius() const { */ void BulletBodyNode:: set_ccd_swept_sphere_radius(PN_stdfloat radius) { + LightMutexHolder holder(BulletWorld::get_global_lock()); return get_object()->setCcdSweptSphereRadius(radius); } @@ -564,6 +760,7 @@ set_ccd_swept_sphere_radius(PN_stdfloat radius) { */ PN_stdfloat BulletBodyNode:: get_ccd_motion_threshold() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return get_object()->getCcdMotionThreshold(); } @@ -573,6 +770,7 @@ get_ccd_motion_threshold() const { */ void BulletBodyNode:: set_ccd_motion_threshold(PN_stdfloat threshold) { + LightMutexHolder holder(BulletWorld::get_global_lock()); return get_object()->setCcdMotionThreshold(threshold); } @@ -582,6 +780,7 @@ set_ccd_motion_threshold(PN_stdfloat threshold) { */ void BulletBodyNode:: add_shapes_from_collision_solids(CollisionNode *cnode) { + LightMutexHolder holder(BulletWorld::get_global_lock()); PT(BulletTriangleMesh) mesh = NULL; @@ -594,7 +793,7 @@ add_shapes_from_collision_solids(CollisionNode *cnode) { CPT(CollisionSphere) sphere = DCAST(CollisionSphere, solid); CPT(TransformState) ts = TransformState::make_pos(sphere->get_center()); - add_shape(BulletSphereShape::make_from_solid(sphere), ts); + do_add_shape(BulletSphereShape::make_from_solid(sphere), ts); } // CollisionBox @@ -602,14 +801,14 @@ add_shapes_from_collision_solids(CollisionNode *cnode) { CPT(CollisionBox) box = DCAST(CollisionBox, solid); CPT(TransformState) ts = TransformState::make_pos(box->get_center()); - add_shape(BulletBoxShape::make_from_solid(box), ts); + do_add_shape(BulletBoxShape::make_from_solid(box), ts); } // CollisionPlane else if (CollisionPlane::get_class_type() == type) { CPT(CollisionPlane) plane = DCAST(CollisionPlane, solid); - add_shape(BulletPlaneShape::make_from_solid(plane)); + do_add_shape(BulletPlaneShape::make_from_solid(plane)); } // CollisionGeom @@ -625,13 +824,13 @@ add_shapes_from_collision_solids(CollisionNode *cnode) { LPoint3 p2 = polygon->get_point(i-1); LPoint3 p3 = polygon->get_point(i); - mesh->add_triangle(p1, p2, p3, true); + mesh->do_add_triangle(p1, p2, p3, true); } } } - if (mesh && mesh->get_num_triangles() > 0) { - add_shape(new BulletTriangleMeshShape(mesh, true)); + if (mesh && mesh->do_get_num_triangles() > 0) { + do_add_shape(new BulletTriangleMeshShape(mesh, true)); } } @@ -651,6 +850,7 @@ set_transform_dirty() { */ BoundingSphere BulletBodyNode:: get_shape_bounds() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); /* btTransform tr; diff --git a/panda/src/bullet/bulletBodyNode.h b/panda/src/bullet/bulletBodyNode.h index 562e88c7fb..55f8583938 100644 --- a/panda/src/bullet/bulletBodyNode.h +++ b/panda/src/bullet/bulletBodyNode.h @@ -42,8 +42,8 @@ PUBLISHED: void add_shape(BulletShape *shape, const TransformState *xform=TransformState::make_identity()); void remove_shape(BulletShape *shape); - INLINE int get_num_shapes() const; - INLINE BulletShape *get_shape(int idx) const; + int get_num_shapes() const; + BulletShape *get_shape(int idx) const; MAKE_SEQ(get_shapes, get_num_shapes, get_shape); LPoint3 get_shape_pos(int idx) const; @@ -54,8 +54,8 @@ PUBLISHED: void add_shapes_from_collision_solids(CollisionNode *cnode); // Static and kinematic - INLINE bool is_static() const; - INLINE bool is_kinematic() const; + bool is_static() const; + bool is_kinematic() const; INLINE void set_static(bool value); INLINE void set_kinematic(bool value); @@ -92,19 +92,19 @@ PUBLISHED: INLINE bool is_debug_enabled() const; // Friction and Restitution - INLINE PN_stdfloat get_restitution() const; - INLINE void set_restitution(PN_stdfloat restitution); + PN_stdfloat get_restitution() const; + void set_restitution(PN_stdfloat restitution); - INLINE PN_stdfloat get_friction() const; - INLINE void set_friction(PN_stdfloat friction); + PN_stdfloat get_friction() const; + void set_friction(PN_stdfloat friction); #if BT_BULLET_VERSION >= 281 - INLINE PN_stdfloat get_rolling_friction() const; - INLINE void set_rolling_friction(PN_stdfloat friction); + PN_stdfloat get_rolling_friction() const; + void set_rolling_friction(PN_stdfloat friction); MAKE_PROPERTY(rolling_friction, get_rolling_friction, set_rolling_friction); #endif - INLINE bool has_anisotropic_friction() const; + bool has_anisotropic_friction() const; void set_anisotropic_friction(const LVecBase3 &friction); LVecBase3 get_anisotropic_friction() const; @@ -151,10 +151,11 @@ public: virtual bool safe_to_flatten_below() const; virtual void output(ostream &out) const; + virtual void do_output(ostream &out) const; protected: - INLINE void set_collision_flag(int flag, bool value); - INLINE bool get_collision_flag(int flag) const; + void set_collision_flag(int flag, bool value); + bool get_collision_flag(int flag) const; btCollisionShape *_shape; @@ -162,7 +163,9 @@ protected: BulletShapes _shapes; private: - virtual void shape_changed(); + virtual void do_shape_changed(); + void do_add_shape(BulletShape *shape, const TransformState *xform=TransformState::make_identity()); + CPT(TransformState) do_get_shape_transform(int idx) const; static bool is_identity(btTransform &trans); diff --git a/panda/src/bullet/bulletBoxShape.I b/panda/src/bullet/bulletBoxShape.I index 384aace8b6..f432db4a69 100644 --- a/panda/src/bullet/bulletBoxShape.I +++ b/panda/src/bullet/bulletBoxShape.I @@ -28,20 +28,3 @@ INLINE BulletBoxShape:: delete _shape; } - -/** - * - */ -INLINE BulletBoxShape:: -BulletBoxShape(const BulletBoxShape ©) : - _shape(copy._shape), _half_extents(copy._half_extents) { -} - -/** - * - */ -INLINE void BulletBoxShape:: -operator = (const BulletBoxShape ©) { - _shape = copy._shape; - _half_extents = copy._half_extents; -} diff --git a/panda/src/bullet/bulletBoxShape.cxx b/panda/src/bullet/bulletBoxShape.cxx index f523184993..9ae2e16a11 100644 --- a/panda/src/bullet/bulletBoxShape.cxx +++ b/panda/src/bullet/bulletBoxShape.cxx @@ -28,6 +28,28 @@ BulletBoxShape(const LVecBase3 &halfExtents) : _half_extents(halfExtents) { _shape->setUserPointer(this); } +/** + * + */ +BulletBoxShape:: +BulletBoxShape(const BulletBoxShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _half_extents = copy._half_extents; +} + +/** + * + */ +void BulletBoxShape:: +operator = (const BulletBoxShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _half_extents = copy._half_extents; +} + /** * */ @@ -42,6 +64,7 @@ ptr() const { */ LVecBase3 BulletBoxShape:: get_half_extents_without_margin() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVecBase3(_shape->getHalfExtentsWithoutMargin()); } @@ -51,6 +74,7 @@ get_half_extents_without_margin() const { */ LVecBase3 BulletBoxShape:: get_half_extents_with_margin() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVecBase3(_shape->getHalfExtentsWithMargin()); } diff --git a/panda/src/bullet/bulletBoxShape.h b/panda/src/bullet/bulletBoxShape.h index d7fa1c5169..bfb9207e61 100644 --- a/panda/src/bullet/bulletBoxShape.h +++ b/panda/src/bullet/bulletBoxShape.h @@ -33,8 +33,8 @@ private: PUBLISHED: explicit BulletBoxShape(const LVecBase3 &halfExtents); - INLINE BulletBoxShape(const BulletBoxShape ©); - INLINE void operator = (const BulletBoxShape ©); + BulletBoxShape(const BulletBoxShape ©); + void operator = (const BulletBoxShape ©); INLINE ~BulletBoxShape(); LVecBase3 get_half_extents_without_margin() const; diff --git a/panda/src/bullet/bulletCapsuleShape.I b/panda/src/bullet/bulletCapsuleShape.I index feb6cdff57..2491d55d0d 100644 --- a/panda/src/bullet/bulletCapsuleShape.I +++ b/panda/src/bullet/bulletCapsuleShape.I @@ -30,26 +30,6 @@ INLINE BulletCapsuleShape:: delete _shape; } -/** - * - */ -INLINE BulletCapsuleShape:: -BulletCapsuleShape(const BulletCapsuleShape ©) : - _shape(copy._shape), - _radius(copy._radius), - _height(copy._height) { -} - -/** - * - */ -INLINE void BulletCapsuleShape:: -operator = (const BulletCapsuleShape ©) { - _shape = copy._shape; - _radius = copy._radius; - _height = copy._height; -} - /** * Returns the radius that was used to construct this capsule. */ diff --git a/panda/src/bullet/bulletCapsuleShape.cxx b/panda/src/bullet/bulletCapsuleShape.cxx index 9f389985e3..1c396bbdfb 100644 --- a/panda/src/bullet/bulletCapsuleShape.cxx +++ b/panda/src/bullet/bulletCapsuleShape.cxx @@ -38,9 +38,35 @@ BulletCapsuleShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up) : break; } + nassertv(_shape); _shape->setUserPointer(this); } +/** + * + */ +BulletCapsuleShape:: +BulletCapsuleShape(const BulletCapsuleShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _radius = copy._radius; + _height = copy._height; +} + +/** + * + */ +void BulletCapsuleShape:: +operator = (const BulletCapsuleShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _radius = copy._radius; + _height = copy._height; +} + + /** * */ @@ -122,6 +148,7 @@ fillin(DatagramIterator &scan, BamReader *manager) { break; } + nassertv(_shape); _shape->setUserPointer(this); _shape->setMargin(margin); } diff --git a/panda/src/bullet/bulletCapsuleShape.h b/panda/src/bullet/bulletCapsuleShape.h index 5b031cc548..e376674976 100644 --- a/panda/src/bullet/bulletCapsuleShape.h +++ b/panda/src/bullet/bulletCapsuleShape.h @@ -30,8 +30,8 @@ private: PUBLISHED: explicit BulletCapsuleShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up); - INLINE BulletCapsuleShape(const BulletCapsuleShape ©); - INLINE void operator = (const BulletCapsuleShape ©); + BulletCapsuleShape(const BulletCapsuleShape ©); + void operator = (const BulletCapsuleShape ©); INLINE ~BulletCapsuleShape(); INLINE PN_stdfloat get_radius() const; diff --git a/panda/src/bullet/bulletCharacterControllerNode.I b/panda/src/bullet/bulletCharacterControllerNode.I index 391e387e67..8017857312 100644 --- a/panda/src/bullet/bulletCharacterControllerNode.I +++ b/panda/src/bullet/bulletCharacterControllerNode.I @@ -17,6 +17,8 @@ INLINE BulletCharacterControllerNode:: ~BulletCharacterControllerNode() { + delete _character; + delete _ghost; } /** diff --git a/panda/src/bullet/bulletCharacterControllerNode.cxx b/panda/src/bullet/bulletCharacterControllerNode.cxx index c6af3842e9..d65948a824 100644 --- a/panda/src/bullet/bulletCharacterControllerNode.cxx +++ b/panda/src/bullet/bulletCharacterControllerNode.cxx @@ -77,6 +77,7 @@ BulletCharacterControllerNode(BulletShape *shape, PN_stdfloat step_height, const */ void BulletCharacterControllerNode:: set_linear_movement(const LVector3 &movement, bool is_local) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!movement.is_nan()); @@ -89,18 +90,19 @@ set_linear_movement(const LVector3 &movement, bool is_local) { */ void BulletCharacterControllerNode:: set_angular_movement(PN_stdfloat omega) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _angular_movement = omega; } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletCharacterControllerNode:: -sync_p2b(PN_stdfloat dt, int num_substeps) { +do_sync_p2b(PN_stdfloat dt, int num_substeps) { // Synchronise global transform - transform_changed(); + do_transform_changed(); // Angular rotation btScalar angle = dt * deg_2_rad(_angular_movement); @@ -131,10 +133,10 @@ sync_p2b(PN_stdfloat dt, int num_substeps) { } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletCharacterControllerNode:: -sync_b2p() { +do_sync_b2p() { NodePath np = NodePath::any_path((PandaNode *)this); LVecBase3 scale = np.get_net_transform()->get_scale(); @@ -154,10 +156,10 @@ sync_b2p() { } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletCharacterControllerNode:: -transform_changed() { +do_transform_changed() { if (_sync_disable) return; @@ -187,10 +189,23 @@ transform_changed() { _ghost->getWorldTransform().setBasis(m); // Set scale - _shape->set_local_scale(scale); + _shape->do_set_local_scale(scale); } } +/** + * + */ +void BulletCharacterControllerNode:: +transform_changed() { + + if (_sync_disable) return; + + LightMutexHolder holder(BulletWorld::get_global_lock()); + + do_transform_changed(); +} + /** * */ @@ -205,6 +220,7 @@ get_shape() const { */ bool BulletCharacterControllerNode:: is_on_ground() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _character->onGround(); } @@ -214,6 +230,7 @@ is_on_ground() const { */ bool BulletCharacterControllerNode:: can_jump() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _character->canJump(); } @@ -223,6 +240,7 @@ can_jump() const { */ void BulletCharacterControllerNode:: do_jump() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _character->jump(); } @@ -232,6 +250,7 @@ do_jump() { */ void BulletCharacterControllerNode:: set_fall_speed(PN_stdfloat fall_speed) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _character->setFallSpeed((btScalar)fall_speed); } @@ -241,6 +260,7 @@ set_fall_speed(PN_stdfloat fall_speed) { */ void BulletCharacterControllerNode:: set_jump_speed(PN_stdfloat jump_speed) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _character->setJumpSpeed((btScalar)jump_speed); } @@ -250,6 +270,7 @@ set_jump_speed(PN_stdfloat jump_speed) { */ void BulletCharacterControllerNode:: set_max_jump_height(PN_stdfloat max_jump_height) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _character->setMaxJumpHeight((btScalar)max_jump_height); } @@ -259,6 +280,7 @@ set_max_jump_height(PN_stdfloat max_jump_height) { */ void BulletCharacterControllerNode:: set_max_slope(PN_stdfloat max_slope) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _character->setMaxSlope((btScalar)max_slope); } @@ -268,6 +290,7 @@ set_max_slope(PN_stdfloat max_slope) { */ PN_stdfloat BulletCharacterControllerNode:: get_max_slope() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_character->getMaxSlope(); } @@ -277,6 +300,8 @@ get_max_slope() const { */ PN_stdfloat BulletCharacterControllerNode:: get_gravity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + #if BT_BULLET_VERSION >= 285 return -(PN_stdfloat)_character->getGravity()[_up]; #else @@ -289,6 +314,8 @@ get_gravity() const { */ void BulletCharacterControllerNode:: set_gravity(PN_stdfloat gravity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + #if BT_BULLET_VERSION >= 285 _character->setGravity(up_vectors[_up] * -(btScalar)gravity); #else @@ -301,6 +328,7 @@ set_gravity(PN_stdfloat gravity) { */ void BulletCharacterControllerNode:: set_use_ghost_sweep_test(bool value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _character->setUseGhostSweepTest(value); -} \ No newline at end of file +} diff --git a/panda/src/bullet/bulletCharacterControllerNode.h b/panda/src/bullet/bulletCharacterControllerNode.h index 0b8574c709..b2c43d8bb4 100644 --- a/panda/src/bullet/bulletCharacterControllerNode.h +++ b/panda/src/bullet/bulletCharacterControllerNode.h @@ -64,8 +64,8 @@ public: INLINE virtual btPairCachingGhostObject *get_ghost() const; INLINE virtual btCharacterControllerInterface *get_character() const; - virtual void sync_p2b(PN_stdfloat dt, int num_substeps); - virtual void sync_b2p(); + virtual void do_sync_p2b(PN_stdfloat dt, int num_substeps); + virtual void do_sync_b2p(); protected: virtual void transform_changed(); @@ -85,6 +85,8 @@ private: bool _linear_movement_is_local; PN_stdfloat _angular_movement; + void do_transform_changed(); + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/bullet/bulletConeShape.I b/panda/src/bullet/bulletConeShape.I index bf618a862b..b9c05664b7 100644 --- a/panda/src/bullet/bulletConeShape.I +++ b/panda/src/bullet/bulletConeShape.I @@ -30,26 +30,6 @@ INLINE BulletConeShape:: delete _shape; } -/** - * - */ -INLINE BulletConeShape:: -BulletConeShape(const BulletConeShape ©) : - _shape(copy._shape), - _radius(copy._radius), - _height(copy._height) { -} - -/** - * - */ -INLINE void BulletConeShape:: -operator = (const BulletConeShape ©) { - _shape = copy._shape; - _radius = copy._radius; - _height = copy._height; -} - /** * Returns the radius that was passed into the constructor. */ diff --git a/panda/src/bullet/bulletConeShape.cxx b/panda/src/bullet/bulletConeShape.cxx index 80e91a038f..30b24a5a18 100644 --- a/panda/src/bullet/bulletConeShape.cxx +++ b/panda/src/bullet/bulletConeShape.cxx @@ -38,9 +38,34 @@ BulletConeShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up) : break; } + nassertv(_shape); _shape->setUserPointer(this); } +/** + * + */ +BulletConeShape:: +BulletConeShape(const BulletConeShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _radius = copy._radius; + _height = copy._height; +} + +/** + * + */ +void BulletConeShape:: +operator = (const BulletConeShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _radius = copy._radius; + _height = copy._height; +} + /** * */ @@ -122,6 +147,7 @@ fillin(DatagramIterator &scan, BamReader *manager) { break; } + nassertv(_shape); _shape->setUserPointer(this); _shape->setMargin(margin); } diff --git a/panda/src/bullet/bulletConeShape.h b/panda/src/bullet/bulletConeShape.h index 081367449b..4d40c61da9 100644 --- a/panda/src/bullet/bulletConeShape.h +++ b/panda/src/bullet/bulletConeShape.h @@ -30,8 +30,8 @@ private: PUBLISHED: explicit BulletConeShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up); - INLINE BulletConeShape(const BulletConeShape ©); - INLINE void operator = (const BulletConeShape ©); + BulletConeShape(const BulletConeShape ©); + void operator = (const BulletConeShape ©); INLINE ~BulletConeShape(); INLINE PN_stdfloat get_radius() const; diff --git a/panda/src/bullet/bulletConeTwistConstraint.I b/panda/src/bullet/bulletConeTwistConstraint.I index a29277462d..9f50b20783 100644 --- a/panda/src/bullet/bulletConeTwistConstraint.I +++ b/panda/src/bullet/bulletConeTwistConstraint.I @@ -19,21 +19,3 @@ INLINE BulletConeTwistConstraint:: delete _constraint; } - -/** - * - */ -INLINE CPT(TransformState) BulletConeTwistConstraint:: -get_frame_a() const { - - return btTrans_to_TransformState(_constraint->getAFrame()); -} - -/** - * - */ -INLINE CPT(TransformState) BulletConeTwistConstraint:: -get_frame_b() const { - - return btTrans_to_TransformState(_constraint->getBFrame()); -} diff --git a/panda/src/bullet/bulletConeTwistConstraint.cxx b/panda/src/bullet/bulletConeTwistConstraint.cxx index 93505b33fc..9b60ab31ce 100644 --- a/panda/src/bullet/bulletConeTwistConstraint.cxx +++ b/panda/src/bullet/bulletConeTwistConstraint.cxx @@ -63,6 +63,7 @@ ptr() const { */ void BulletConeTwistConstraint:: set_limit(int index, PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); value = deg_2_rad(value); @@ -74,6 +75,7 @@ set_limit(int index, PN_stdfloat value) { */ void BulletConeTwistConstraint:: set_limit(PN_stdfloat swing1, PN_stdfloat swing2, PN_stdfloat twist, PN_stdfloat softness, PN_stdfloat bias, PN_stdfloat relaxation) { + LightMutexHolder holder(BulletWorld::get_global_lock()); swing1 = deg_2_rad(swing1); swing2 = deg_2_rad(swing2); @@ -87,6 +89,7 @@ set_limit(PN_stdfloat swing1, PN_stdfloat swing2, PN_stdfloat twist, PN_stdfloat */ void BulletConeTwistConstraint:: set_damping(PN_stdfloat damping) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setDamping(damping); } @@ -96,6 +99,7 @@ set_damping(PN_stdfloat damping) { */ PN_stdfloat BulletConeTwistConstraint:: get_fix_threshold() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _constraint->getFixThresh(); } @@ -105,6 +109,7 @@ get_fix_threshold() const { */ void BulletConeTwistConstraint:: set_fix_threshold(PN_stdfloat threshold) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setFixThresh(threshold); } @@ -114,6 +119,7 @@ set_fix_threshold(PN_stdfloat threshold) { */ void BulletConeTwistConstraint:: enable_motor(bool enable) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->enableMotor(enable); } @@ -123,6 +129,7 @@ enable_motor(bool enable) { */ void BulletConeTwistConstraint:: set_max_motor_impulse(PN_stdfloat max_impulse) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMaxMotorImpulse(max_impulse); } @@ -132,6 +139,7 @@ set_max_motor_impulse(PN_stdfloat max_impulse) { */ void BulletConeTwistConstraint:: set_max_motor_impulse_normalized(PN_stdfloat max_impulse) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMaxMotorImpulseNormalized(max_impulse); } @@ -141,6 +149,7 @@ set_max_motor_impulse_normalized(PN_stdfloat max_impulse) { */ void BulletConeTwistConstraint:: set_motor_target(const LQuaternion &quat) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMotorTarget(LQuaternion_to_btQuat(quat)); } @@ -150,6 +159,7 @@ set_motor_target(const LQuaternion &quat) { */ void BulletConeTwistConstraint:: set_motor_target_in_constraint_space(const LQuaternion &quat) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMotorTargetInConstraintSpace(LQuaternion_to_btQuat(quat)); } @@ -159,9 +169,30 @@ set_motor_target_in_constraint_space(const LQuaternion &quat) { */ void BulletConeTwistConstraint:: set_frames(const TransformState *ts_a, const TransformState *ts_b) { + LightMutexHolder holder(BulletWorld::get_global_lock()); btTransform frame_a = TransformState_to_btTrans(ts_a); btTransform frame_b = TransformState_to_btTrans(ts_b); _constraint->setFrames(frame_a, frame_b); } + +/** + * + */ +CPT(TransformState) BulletConeTwistConstraint:: +get_frame_a() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_constraint->getAFrame()); +} + +/** + * + */ +CPT(TransformState) BulletConeTwistConstraint:: +get_frame_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_constraint->getBFrame()); +} diff --git a/panda/src/bullet/bulletConeTwistConstraint.h b/panda/src/bullet/bulletConeTwistConstraint.h index bc4f548dfb..d7fabee508 100644 --- a/panda/src/bullet/bulletConeTwistConstraint.h +++ b/panda/src/bullet/bulletConeTwistConstraint.h @@ -53,8 +53,8 @@ PUBLISHED: void set_motor_target_in_constraint_space(const LQuaternion &quat); void set_frames(const TransformState *ts_a, const TransformState *ts_b); - INLINE CPT(TransformState) get_frame_a() const; - INLINE CPT(TransformState) get_frame_b() const; + CPT(TransformState) get_frame_a() const; + CPT(TransformState) get_frame_b() const; MAKE_PROPERTY(fix_threshold, get_fix_threshold, set_fix_threshold); MAKE_PROPERTY(frame_a, get_frame_a); diff --git a/panda/src/bullet/bulletConvexHullShape.I b/panda/src/bullet/bulletConvexHullShape.I index 71fa3cd190..2016b38e4d 100644 --- a/panda/src/bullet/bulletConvexHullShape.I +++ b/panda/src/bullet/bulletConvexHullShape.I @@ -19,19 +19,3 @@ INLINE BulletConvexHullShape:: delete _shape; } - -/** - * - */ -INLINE BulletConvexHullShape:: -BulletConvexHullShape(const BulletConvexHullShape ©) : - _shape(copy._shape) { -} - -/** - * - */ -INLINE void BulletConvexHullShape:: -operator = (const BulletConvexHullShape ©) { - _shape = copy._shape; -} diff --git a/panda/src/bullet/bulletConvexHullShape.cxx b/panda/src/bullet/bulletConvexHullShape.cxx index 9b3df8dfe2..c796072b4a 100644 --- a/panda/src/bullet/bulletConvexHullShape.cxx +++ b/panda/src/bullet/bulletConvexHullShape.cxx @@ -29,6 +29,26 @@ BulletConvexHullShape() { _shape->setUserPointer(this); } +/** + * + */ +BulletConvexHullShape:: +BulletConvexHullShape(const BulletConvexHullShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; +} + +/** + * + */ +void BulletConvexHullShape:: +operator = (const BulletConvexHullShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; +} + /** * */ @@ -43,6 +63,7 @@ ptr() const { */ void BulletConvexHullShape:: add_point(const LPoint3 &p) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _shape->addPoint(LVecBase3_to_btVector3(p)); } @@ -52,6 +73,10 @@ add_point(const LPoint3 &p) { */ void BulletConvexHullShape:: add_array(const PTA_LVecBase3 &points) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + if (_shape) + delete _shape; _shape = new btConvexHullShape(NULL, 0); _shape->setUserPointer(this); @@ -75,6 +100,7 @@ add_array(const PTA_LVecBase3 &points) { */ void BulletConvexHullShape:: add_geom(const Geom *geom, const TransformState *ts) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(geom); nassertv(ts); diff --git a/panda/src/bullet/bulletConvexHullShape.h b/panda/src/bullet/bulletConvexHullShape.h index f74489bd0e..653c044952 100644 --- a/panda/src/bullet/bulletConvexHullShape.h +++ b/panda/src/bullet/bulletConvexHullShape.h @@ -29,8 +29,8 @@ class EXPCL_PANDABULLET BulletConvexHullShape : public BulletShape { PUBLISHED: BulletConvexHullShape(); - INLINE BulletConvexHullShape(const BulletConvexHullShape ©); - INLINE void operator = (const BulletConvexHullShape ©); + BulletConvexHullShape(const BulletConvexHullShape ©); + void operator = (const BulletConvexHullShape ©); INLINE ~BulletConvexHullShape(); void add_point(const LPoint3 &p); diff --git a/panda/src/bullet/bulletConvexPointCloudShape.I b/panda/src/bullet/bulletConvexPointCloudShape.I index 909dfd2d7e..47e0b62704 100644 --- a/panda/src/bullet/bulletConvexPointCloudShape.I +++ b/panda/src/bullet/bulletConvexPointCloudShape.I @@ -28,30 +28,3 @@ INLINE BulletConvexPointCloudShape:: delete _shape; } - -/** - * - */ -INLINE BulletConvexPointCloudShape:: -BulletConvexPointCloudShape(const BulletConvexPointCloudShape ©) : - _scale(copy._scale), - _shape(copy._shape) { -} - -/** - * - */ -INLINE void BulletConvexPointCloudShape:: -operator = (const BulletConvexPointCloudShape ©) { - _scale = copy._scale; - _shape = copy._shape; -} - -/** - * - */ -INLINE int BulletConvexPointCloudShape:: -get_num_points() const { - - return _shape->getNumPoints(); -} diff --git a/panda/src/bullet/bulletConvexPointCloudShape.cxx b/panda/src/bullet/bulletConvexPointCloudShape.cxx index 1527a712b3..fb7f62287b 100644 --- a/panda/src/bullet/bulletConvexPointCloudShape.cxx +++ b/panda/src/bullet/bulletConvexPointCloudShape.cxx @@ -84,6 +84,38 @@ BulletConvexPointCloudShape(const Geom *geom, LVecBase3 scale) { _shape->setUserPointer(this); } +/** + * + */ +BulletConvexPointCloudShape:: +BulletConvexPointCloudShape(const BulletConvexPointCloudShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _scale = copy._scale; + _shape = copy._shape; +} + +/** + * + */ +void BulletConvexPointCloudShape:: +operator = (const BulletConvexPointCloudShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _scale = copy._scale; + _shape = copy._shape; +} + +/** + * + */ +int BulletConvexPointCloudShape:: +get_num_points() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _shape->getNumPoints(); +} + /** * Tells the BamReader how to create objects of type BulletShape. */ diff --git a/panda/src/bullet/bulletConvexPointCloudShape.h b/panda/src/bullet/bulletConvexPointCloudShape.h index 293ece996b..da403d8794 100644 --- a/panda/src/bullet/bulletConvexPointCloudShape.h +++ b/panda/src/bullet/bulletConvexPointCloudShape.h @@ -33,11 +33,11 @@ private: PUBLISHED: explicit BulletConvexPointCloudShape(const PTA_LVecBase3 &points, LVecBase3 scale=LVecBase3(1.)); explicit BulletConvexPointCloudShape(const Geom *geom, LVecBase3 scale=LVecBase3(1.)); - INLINE BulletConvexPointCloudShape(const BulletConvexPointCloudShape ©); - INLINE void operator = (const BulletConvexPointCloudShape ©); + BulletConvexPointCloudShape(const BulletConvexPointCloudShape ©); + void operator = (const BulletConvexPointCloudShape ©); INLINE ~BulletConvexPointCloudShape(); - INLINE int get_num_points() const; + int get_num_points() const; MAKE_PROPERTY(num_points, get_num_points); diff --git a/panda/src/bullet/bulletCylinderShape.I b/panda/src/bullet/bulletCylinderShape.I index c389813444..54616905f9 100644 --- a/panda/src/bullet/bulletCylinderShape.I +++ b/panda/src/bullet/bulletCylinderShape.I @@ -28,47 +28,3 @@ INLINE BulletCylinderShape:: delete _shape; } - -/** - * - */ -INLINE BulletCylinderShape:: -BulletCylinderShape(const BulletCylinderShape ©) : - _shape(copy._shape), _half_extents(copy._half_extents) { -} - -/** - * - */ -INLINE void BulletCylinderShape:: -operator = (const BulletCylinderShape ©) { - _shape = copy._shape; - _half_extents = copy._half_extents; -} - -/** - * - */ -INLINE PN_stdfloat BulletCylinderShape:: -get_radius() const { - - return (PN_stdfloat)_shape->getRadius(); -} - -/** - * - */ -INLINE LVecBase3 BulletCylinderShape:: -get_half_extents_without_margin() const { - - return btVector3_to_LVecBase3(_shape->getHalfExtentsWithoutMargin()); -} - -/** - * - */ -INLINE LVecBase3 BulletCylinderShape:: -get_half_extents_with_margin() const { - - return btVector3_to_LVecBase3(_shape->getHalfExtentsWithMargin()); -} diff --git a/panda/src/bullet/bulletCylinderShape.cxx b/panda/src/bullet/bulletCylinderShape.cxx index fec07ce484..8f95bc5591 100644 --- a/panda/src/bullet/bulletCylinderShape.cxx +++ b/panda/src/bullet/bulletCylinderShape.cxx @@ -39,6 +39,7 @@ BulletCylinderShape(const LVector3 &half_extents, BulletUpAxis up) : break; } + nassertv(_shape); _shape->setUserPointer(this); } @@ -66,9 +67,32 @@ BulletCylinderShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up) { break; } + nassertv(_shape); _shape->setUserPointer(this); } +/** + * + */ +BulletCylinderShape:: +BulletCylinderShape(const BulletCylinderShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _half_extents = copy._half_extents; +} + +/** + * + */ +void BulletCylinderShape:: +operator = (const BulletCylinderShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _half_extents = copy._half_extents; +} + /** * */ @@ -78,6 +102,36 @@ ptr() const { return _shape; } +/** + * + */ +PN_stdfloat BulletCylinderShape:: +get_radius() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_shape->getRadius(); +} + +/** + * + */ +LVecBase3 BulletCylinderShape:: +get_half_extents_without_margin() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVecBase3(_shape->getHalfExtentsWithoutMargin()); +} + +/** + * + */ +LVecBase3 BulletCylinderShape:: +get_half_extents_with_margin() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVecBase3(_shape->getHalfExtentsWithMargin()); +} + /** * Tells the BamReader how to create objects of type BulletShape. */ @@ -150,6 +204,7 @@ fillin(DatagramIterator &scan, BamReader *manager) { break; } + nassertv(_shape); _shape->setUserPointer(this); _shape->setMargin(margin); } diff --git a/panda/src/bullet/bulletCylinderShape.h b/panda/src/bullet/bulletCylinderShape.h index 2cdd854ac4..f53962ec6f 100644 --- a/panda/src/bullet/bulletCylinderShape.h +++ b/panda/src/bullet/bulletCylinderShape.h @@ -31,13 +31,13 @@ private: PUBLISHED: explicit BulletCylinderShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up); explicit BulletCylinderShape(const LVector3 &half_extents, BulletUpAxis up=Z_up); - INLINE BulletCylinderShape(const BulletCylinderShape ©); - INLINE void operator = (const BulletCylinderShape ©); + BulletCylinderShape(const BulletCylinderShape ©); + void operator = (const BulletCylinderShape ©); INLINE ~BulletCylinderShape(); - INLINE PN_stdfloat get_radius() const; - INLINE LVecBase3 get_half_extents_without_margin() const; - INLINE LVecBase3 get_half_extents_with_margin() const; + PN_stdfloat get_radius() const; + LVecBase3 get_half_extents_without_margin() const; + LVecBase3 get_half_extents_with_margin() const; MAKE_PROPERTY(radius, get_radius); MAKE_PROPERTY(half_extents_without_margin, get_half_extents_without_margin); diff --git a/panda/src/bullet/bulletDebugNode.cxx b/panda/src/bullet/bulletDebugNode.cxx index 9b8c9c0bfb..743b5163ff 100644 --- a/panda/src/bullet/bulletDebugNode.cxx +++ b/panda/src/bullet/bulletDebugNode.cxx @@ -169,7 +169,7 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) { PT(Geom) debug_triangles; { - LightMutexHolder holder(_lock); + LightMutexHolder holder(BulletWorld::get_global_lock()); if (_debug_world == nullptr) { return; } @@ -270,8 +270,7 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) { * */ void BulletDebugNode:: -sync_b2p(btDynamicsWorld *world) { - LightMutexHolder holder(_lock); +do_sync_b2p(btDynamicsWorld *world) { _debug_world = world; _debug_stale = true; diff --git a/panda/src/bullet/bulletDebugNode.h b/panda/src/bullet/bulletDebugNode.h index dabe9d13fa..c780429a79 100644 --- a/panda/src/bullet/bulletDebugNode.h +++ b/panda/src/bullet/bulletDebugNode.h @@ -17,7 +17,6 @@ #include "pandabase.h" #include "bullet_includes.h" -#include "lightMutex.h" /** * @@ -55,7 +54,7 @@ public: virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data); private: - void sync_b2p(btDynamicsWorld *world); + void do_sync_b2p(btDynamicsWorld *world); struct Line { LVecBase3 _p0; @@ -101,7 +100,6 @@ private: int _mode; }; - LightMutex _lock; DebugDraw _drawer; bool _debug_stale; diff --git a/panda/src/bullet/bulletGenericConstraint.I b/panda/src/bullet/bulletGenericConstraint.I index 08ee24ccba..da9a6fbc49 100644 --- a/panda/src/bullet/bulletGenericConstraint.I +++ b/panda/src/bullet/bulletGenericConstraint.I @@ -19,21 +19,3 @@ INLINE BulletGenericConstraint:: delete _constraint; } - -/** - * - */ -INLINE CPT(TransformState) BulletGenericConstraint:: -get_frame_a() const { - - return btTrans_to_TransformState(_constraint->getFrameOffsetA()); -} - -/** - * - */ -INLINE CPT(TransformState) BulletGenericConstraint:: -get_frame_b() const { - - return btTrans_to_TransformState(_constraint->getFrameOffsetB()); -} diff --git a/panda/src/bullet/bulletGenericConstraint.cxx b/panda/src/bullet/bulletGenericConstraint.cxx index a68a345ce6..de98b5d40b 100644 --- a/panda/src/bullet/bulletGenericConstraint.cxx +++ b/panda/src/bullet/bulletGenericConstraint.cxx @@ -63,6 +63,7 @@ ptr() const { */ LVector3 BulletGenericConstraint:: get_axis(int axis) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertr(axis >= 0, LVector3::zero()); nassertr(axis <= 3, LVector3::zero()); @@ -76,6 +77,7 @@ get_axis(int axis) const { */ PN_stdfloat BulletGenericConstraint:: get_pivot(int axis) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertr(axis >= 0, 0.0f); nassertr(axis <= 3, 0.0f); @@ -89,6 +91,7 @@ get_pivot(int axis) const { */ PN_stdfloat BulletGenericConstraint:: get_angle(int axis) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertr(axis >= 0, 0.0f); nassertr(axis <= 3, 0.0f); @@ -102,6 +105,7 @@ get_angle(int axis) const { */ void BulletGenericConstraint:: set_linear_limit(int axis, PN_stdfloat low, PN_stdfloat high) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(axis >= 0); nassertv(axis <= 3); @@ -115,6 +119,7 @@ set_linear_limit(int axis, PN_stdfloat low, PN_stdfloat high) { */ void BulletGenericConstraint:: set_angular_limit(int axis, PN_stdfloat low, PN_stdfloat high) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(axis >= 0); nassertv(axis <= 3); @@ -126,11 +131,32 @@ set_angular_limit(int axis, PN_stdfloat low, PN_stdfloat high) { _constraint->setLimit(axis + 3, low, high); } +/** + * + */ +CPT(TransformState) BulletGenericConstraint:: +get_frame_a() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_constraint->getFrameOffsetA()); +} + +/** + * + */ +CPT(TransformState) BulletGenericConstraint:: +get_frame_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_constraint->getFrameOffsetB()); +} + /** * */ BulletRotationalLimitMotor BulletGenericConstraint:: get_rotational_limit_motor(int axis) { + LightMutexHolder holder(BulletWorld::get_global_lock()); return BulletRotationalLimitMotor(*_constraint->getRotationalLimitMotor(axis)); } @@ -140,6 +166,7 @@ get_rotational_limit_motor(int axis) { */ BulletTranslationalLimitMotor BulletGenericConstraint:: get_translational_limit_motor() { + LightMutexHolder holder(BulletWorld::get_global_lock()); return BulletTranslationalLimitMotor(*_constraint->getTranslationalLimitMotor()); } @@ -149,6 +176,7 @@ get_translational_limit_motor() { */ void BulletGenericConstraint:: set_frames(const TransformState *ts_a, const TransformState *ts_b) { + LightMutexHolder holder(BulletWorld::get_global_lock()); btTransform frame_a = TransformState_to_btTrans(ts_a); btTransform frame_b = TransformState_to_btTrans(ts_b); diff --git a/panda/src/bullet/bulletGenericConstraint.h b/panda/src/bullet/bulletGenericConstraint.h index a2446ffcd4..f571df1ad6 100644 --- a/panda/src/bullet/bulletGenericConstraint.h +++ b/panda/src/bullet/bulletGenericConstraint.h @@ -57,8 +57,8 @@ PUBLISHED: // Frames void set_frames(const TransformState *ts_a, const TransformState *ts_b); - INLINE CPT(TransformState) get_frame_a() const; - INLINE CPT(TransformState) get_frame_b() const; + CPT(TransformState) get_frame_a() const; + CPT(TransformState) get_frame_b() const; MAKE_PROPERTY(translational_limit_motor, get_translational_limit_motor); MAKE_PROPERTY(frame_a, get_frame_a); diff --git a/panda/src/bullet/bulletGhostNode.I b/panda/src/bullet/bulletGhostNode.I index c4620097a8..d9b24a0950 100644 --- a/panda/src/bullet/bulletGhostNode.I +++ b/panda/src/bullet/bulletGhostNode.I @@ -20,23 +20,3 @@ INLINE BulletGhostNode:: delete _ghost; } -/** - * - */ -INLINE int BulletGhostNode:: -get_num_overlapping_nodes() const { - - return _ghost->getNumOverlappingObjects(); -} - -/** - * - */ -INLINE PandaNode *BulletGhostNode:: -get_overlapping_node(int idx) const { - - nassertr(idx >=0 && idx < _ghost->getNumOverlappingObjects(), NULL); - - btCollisionObject *object = _ghost->getOverlappingObject(idx); - return (object) ? (PandaNode *)object->getUserPointer() : NULL; -} diff --git a/panda/src/bullet/bulletGhostNode.cxx b/panda/src/bullet/bulletGhostNode.cxx index ddead41067..39740ff5a1 100644 --- a/panda/src/bullet/bulletGhostNode.cxx +++ b/panda/src/bullet/bulletGhostNode.cxx @@ -53,6 +53,7 @@ get_object() const { */ void BulletGhostNode:: parents_changed() { + LightMutexHolder holder(BulletWorld::get_global_lock()); Parents parents = get_parents(); for (size_t i = 0; i < parents.get_num_parents(); ++i) { @@ -73,10 +74,10 @@ parents_changed() { } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletGhostNode:: -transform_changed() { +do_transform_changed() { if (_sync_disable) return; @@ -98,27 +99,60 @@ transform_changed() { if (!scale.almost_equal(LVecBase3(1.0f, 1.0f, 1.0f))) { for (int i=0; iset_local_scale(scale); + shape->do_set_local_scale(scale); } } } } } -/** - * - */ void BulletGhostNode:: -sync_p2b() { +transform_changed() { - transform_changed(); + if (_sync_disable) return; + + LightMutexHolder holder(BulletWorld::get_global_lock()); + + do_transform_changed(); } /** * */ +int BulletGhostNode:: +get_num_overlapping_nodes() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _ghost->getNumOverlappingObjects(); +} + +/** + * + */ +PandaNode *BulletGhostNode:: +get_overlapping_node(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertr(idx >=0 && idx < _ghost->getNumOverlappingObjects(), NULL); + + btCollisionObject *object = _ghost->getOverlappingObject(idx); + return (object) ? (PandaNode *)object->getUserPointer() : NULL; +} + +/** + * Assumes the lock(bullet global lock) is held by the caller + */ void BulletGhostNode:: -sync_b2p() { +do_sync_p2b() { + + do_transform_changed(); +} + +/** + * Assumes the lock(bullet global lock) is held by the caller + */ +void BulletGhostNode:: +do_sync_b2p() { NodePath np = NodePath::any_path((PandaNode *)this); LVecBase3 scale = np.get_net_transform()->get_scale(); diff --git a/panda/src/bullet/bulletGhostNode.h b/panda/src/bullet/bulletGhostNode.h index 87a1624f3d..a76c0ce8a1 100644 --- a/panda/src/bullet/bulletGhostNode.h +++ b/panda/src/bullet/bulletGhostNode.h @@ -34,8 +34,8 @@ PUBLISHED: INLINE ~BulletGhostNode(); // Overlapping - INLINE int get_num_overlapping_nodes() const; - INLINE PandaNode *get_overlapping_node(int idx) const; + int get_num_overlapping_nodes() const; + PandaNode *get_overlapping_node(int idx) const; MAKE_SEQ(get_overlapping_nodes, get_num_overlapping_nodes, get_overlapping_node); MAKE_SEQ_PROPERTY(overlapping_nodes, get_num_overlapping_nodes, get_overlapping_node); @@ -43,8 +43,8 @@ PUBLISHED: public: virtual btCollisionObject *get_object() const; - void sync_p2b(); - void sync_b2p(); + void do_sync_p2b(); + void do_sync_b2p(); protected: virtual void parents_changed(); @@ -57,6 +57,8 @@ private: btPairCachingGhostObject *_ghost; + void do_transform_changed(); + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/bullet/bulletHeightfieldShape.I b/panda/src/bullet/bulletHeightfieldShape.I index 5bfceb1e5b..5a2702be9a 100644 --- a/panda/src/bullet/bulletHeightfieldShape.I +++ b/panda/src/bullet/bulletHeightfieldShape.I @@ -32,33 +32,3 @@ INLINE BulletHeightfieldShape:: delete _shape; delete [] _data; } - -/** - * - */ -INLINE BulletHeightfieldShape:: -BulletHeightfieldShape(const BulletHeightfieldShape ©) : - _shape(copy._shape), - _num_rows(copy._num_rows), - _num_cols(copy._num_cols), - _max_height(copy._max_height), - _up(copy._up) { - - size_t size = (size_t)_num_rows * (size_t)_num_cols; - _data = new btScalar[size]; - memcpy(_data, copy._data, size * sizeof(btScalar)); -} - -/** - * - */ -INLINE void BulletHeightfieldShape:: -operator = (const BulletHeightfieldShape ©) { - _shape = copy._shape; - _num_rows = copy._num_rows; - _num_cols = copy._num_cols; - - size_t size = (size_t)_num_rows * (size_t)_num_cols; - _data = new btScalar[size]; - memcpy(_data, copy._data, size * sizeof(btScalar)); -} diff --git a/panda/src/bullet/bulletHeightfieldShape.cxx b/panda/src/bullet/bulletHeightfieldShape.cxx index 6ff476c0a6..1a01876401 100644 --- a/panda/src/bullet/bulletHeightfieldShape.cxx +++ b/panda/src/bullet/bulletHeightfieldShape.cxx @@ -61,6 +61,7 @@ ptr() const { */ void BulletHeightfieldShape:: set_use_diamond_subdivision(bool flag) { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _shape->setUseDiamondSubdivision(flag); } @@ -104,6 +105,42 @@ BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up) : _shape->setUserPointer(this); } +/** + * + */ +BulletHeightfieldShape:: +BulletHeightfieldShape(const BulletHeightfieldShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _num_rows = copy._num_rows; + _num_cols = copy._num_cols; + _max_height = copy._max_height; + _up = copy._up; + + size_t size = (size_t)_num_rows * (size_t)_num_cols; + _data = new btScalar[size]; + memcpy(_data, copy._data, size * sizeof(btScalar)); +} + +/** + * + */ +void BulletHeightfieldShape:: +operator = (const BulletHeightfieldShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _num_rows = copy._num_rows; + _num_cols = copy._num_cols; + _max_height = copy._max_height; + _up = copy._up; + + size_t size = (size_t)_num_rows * (size_t)_num_cols; + _data = new btScalar[size]; + memcpy(_data, copy._data, size * sizeof(btScalar)); +} + /** * Tells the BamReader how to create objects of type BulletShape. */ @@ -169,8 +206,9 @@ fillin(DatagramIterator &scan, BamReader *manager) { _num_cols = scan.get_int32(); size_t size = (size_t)_num_rows * (size_t)_num_cols; - delete[] _data; + delete [] _data; _data = new float[size]; + for (size_t i = 0; i < size; ++i) { _data[i] = scan.get_stdfloat(); } diff --git a/panda/src/bullet/bulletHeightfieldShape.h b/panda/src/bullet/bulletHeightfieldShape.h index 2f70fddac8..1bfc6c41c3 100644 --- a/panda/src/bullet/bulletHeightfieldShape.h +++ b/panda/src/bullet/bulletHeightfieldShape.h @@ -34,8 +34,8 @@ private: PUBLISHED: explicit BulletHeightfieldShape(const PNMImage &image, PN_stdfloat max_height, BulletUpAxis up=Z_up); explicit BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up=Z_up); - INLINE BulletHeightfieldShape(const BulletHeightfieldShape ©); - INLINE void operator = (const BulletHeightfieldShape ©); + BulletHeightfieldShape(const BulletHeightfieldShape ©); + void operator = (const BulletHeightfieldShape ©); INLINE ~BulletHeightfieldShape(); void set_use_diamond_subdivision(bool flag=true); diff --git a/panda/src/bullet/bulletHingeConstraint.I b/panda/src/bullet/bulletHingeConstraint.I index 01614f603a..e5753a5947 100644 --- a/panda/src/bullet/bulletHingeConstraint.I +++ b/panda/src/bullet/bulletHingeConstraint.I @@ -19,21 +19,3 @@ INLINE BulletHingeConstraint:: delete _constraint; } - -/** - * - */ -INLINE CPT(TransformState) BulletHingeConstraint:: -get_frame_a() const { - - return btTrans_to_TransformState(_constraint->getAFrame()); -} - -/** - * - */ -INLINE CPT(TransformState) BulletHingeConstraint:: -get_frame_b() const { - - return btTrans_to_TransformState(_constraint->getBFrame()); -} diff --git a/panda/src/bullet/bulletHingeConstraint.cxx b/panda/src/bullet/bulletHingeConstraint.cxx index a262be88e8..5cbfa44d94 100644 --- a/panda/src/bullet/bulletHingeConstraint.cxx +++ b/panda/src/bullet/bulletHingeConstraint.cxx @@ -111,6 +111,7 @@ ptr() const { */ void BulletHingeConstraint:: set_angular_only(bool value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _constraint->setAngularOnly(value); } @@ -120,6 +121,7 @@ set_angular_only(bool value) { */ bool BulletHingeConstraint:: get_angular_only() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _constraint->getAngularOnly(); } @@ -129,6 +131,7 @@ get_angular_only() const { */ void BulletHingeConstraint:: set_limit(PN_stdfloat low, PN_stdfloat high, PN_stdfloat softness, PN_stdfloat bias, PN_stdfloat relaxation) { + LightMutexHolder holder(BulletWorld::get_global_lock()); low = deg_2_rad(low); high = deg_2_rad(high); @@ -141,6 +144,7 @@ set_limit(PN_stdfloat low, PN_stdfloat high, PN_stdfloat softness, PN_stdfloat b */ void BulletHingeConstraint:: set_axis(const LVector3 &axis) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!axis.is_nan()); @@ -153,6 +157,7 @@ set_axis(const LVector3 &axis) { */ PN_stdfloat BulletHingeConstraint:: get_lower_limit() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return rad_2_deg(_constraint->getLowerLimit()); } @@ -162,6 +167,7 @@ get_lower_limit() const { */ PN_stdfloat BulletHingeConstraint:: get_upper_limit() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return rad_2_deg(_constraint->getUpperLimit()); } @@ -171,6 +177,7 @@ get_upper_limit() const { */ PN_stdfloat BulletHingeConstraint:: get_hinge_angle() { + LightMutexHolder holder(BulletWorld::get_global_lock()); return rad_2_deg(_constraint->getHingeAngle()); } @@ -184,6 +191,7 @@ get_hinge_angle() { */ void BulletHingeConstraint:: enable_angular_motor(bool enable, PN_stdfloat target_velocity, PN_stdfloat max_impulse) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->enableAngularMotor(enable, target_velocity, max_impulse); } @@ -193,6 +201,7 @@ enable_angular_motor(bool enable, PN_stdfloat target_velocity, PN_stdfloat max_i */ void BulletHingeConstraint:: enable_motor(bool enable) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->enableMotor(enable); } @@ -203,6 +212,7 @@ enable_motor(bool enable) { */ void BulletHingeConstraint:: set_max_motor_impulse(PN_stdfloat max_impulse) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMaxMotorImpulse(max_impulse); } @@ -212,6 +222,7 @@ set_max_motor_impulse(PN_stdfloat max_impulse) { */ void BulletHingeConstraint:: set_motor_target(const LQuaternion &quat, PN_stdfloat dt) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMotorTarget(LQuaternion_to_btQuat(quat), dt); } @@ -221,6 +232,7 @@ set_motor_target(const LQuaternion &quat, PN_stdfloat dt) { */ void BulletHingeConstraint:: set_motor_target(PN_stdfloat target_angle, PN_stdfloat dt) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMotorTarget(target_angle, dt); } @@ -230,9 +242,30 @@ set_motor_target(PN_stdfloat target_angle, PN_stdfloat dt) { */ void BulletHingeConstraint:: set_frames(const TransformState *ts_a, const TransformState *ts_b) { + LightMutexHolder holder(BulletWorld::get_global_lock()); btTransform frame_a = TransformState_to_btTrans(ts_a); btTransform frame_b = TransformState_to_btTrans(ts_b); _constraint->setFrames(frame_a, frame_b); } + +/** + * + */ +CPT(TransformState) BulletHingeConstraint:: +get_frame_a() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_constraint->getAFrame()); +} + +/** + * + */ +CPT(TransformState) BulletHingeConstraint:: +get_frame_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_constraint->getBFrame()); +} diff --git a/panda/src/bullet/bulletHingeConstraint.h b/panda/src/bullet/bulletHingeConstraint.h index 12a1b1b2c2..dc6768490b 100644 --- a/panda/src/bullet/bulletHingeConstraint.h +++ b/panda/src/bullet/bulletHingeConstraint.h @@ -69,8 +69,8 @@ PUBLISHED: void set_motor_target(PN_stdfloat target_angle, PN_stdfloat dt); void set_frames(const TransformState *ts_a, const TransformState *ts_b); - INLINE CPT(TransformState) get_frame_a() const; - INLINE CPT(TransformState) get_frame_b() const; + CPT(TransformState) get_frame_a() const; + CPT(TransformState) get_frame_b() const; MAKE_PROPERTY(hinge_angle, get_hinge_angle); MAKE_PROPERTY(lower_limit, get_lower_limit); diff --git a/panda/src/bullet/bulletManifoldPoint.I b/panda/src/bullet/bulletManifoldPoint.I index 6730d2f349..4347bad4f4 100644 --- a/panda/src/bullet/bulletManifoldPoint.I +++ b/panda/src/bullet/bulletManifoldPoint.I @@ -18,228 +18,3 @@ INLINE BulletManifoldPoint:: ~BulletManifoldPoint() { } - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_lateral_friction_initialized(bool value) { -#if BT_BULLET_VERSION >= 285 - if (value) { - _pt.m_contactPointFlags |= BT_CONTACT_FLAG_LATERAL_FRICTION_INITIALIZED; - } else { - _pt.m_contactPointFlags &= ~BT_CONTACT_FLAG_LATERAL_FRICTION_INITIALIZED; - } -#else - _pt.m_lateralFrictionInitialized = value; -#endif -} - -/** - * - */ -INLINE bool BulletManifoldPoint:: -get_lateral_friction_initialized() const { -#if BT_BULLET_VERSION >= 285 - return (_pt.m_contactPointFlags & BT_CONTACT_FLAG_LATERAL_FRICTION_INITIALIZED) != 0; -#else - return _pt.m_lateralFrictionInitialized; -#endif -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_lateral_friction_dir1(const LVecBase3 &dir) { - - _pt.m_lateralFrictionDir1 = LVecBase3_to_btVector3(dir); -} - -/** - * - */ -INLINE LVector3 BulletManifoldPoint:: -get_lateral_friction_dir1() const { - - return btVector3_to_LVector3(_pt.m_lateralFrictionDir1); -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_lateral_friction_dir2(const LVecBase3 &dir) { - - _pt.m_lateralFrictionDir2 = LVecBase3_to_btVector3(dir); -} - -/** - * - */ -INLINE LVector3 BulletManifoldPoint:: -get_lateral_friction_dir2() const { - - return btVector3_to_LVector3(_pt.m_lateralFrictionDir2); -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_contact_motion1(PN_stdfloat value) { - - _pt.m_contactMotion1 = (btScalar)value; -} - -/** - * - */ -INLINE PN_stdfloat BulletManifoldPoint:: -get_contact_motion1() const { - - return (PN_stdfloat)_pt.m_contactMotion1; -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_contact_motion2(PN_stdfloat value) { - - _pt.m_contactMotion2 = (btScalar)value; -} - -/** - * - */ -INLINE PN_stdfloat BulletManifoldPoint:: -get_contact_motion2() const { - - return (PN_stdfloat)_pt.m_contactMotion2; -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_combined_friction(PN_stdfloat value) { - - _pt.m_combinedFriction = (btScalar)value; -} - -/** - * - */ -INLINE PN_stdfloat BulletManifoldPoint:: -get_combined_friction() const { - - return (PN_stdfloat)_pt.m_combinedFriction; -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_combined_restitution(PN_stdfloat value) { - - _pt.m_combinedRestitution = (btScalar)value; -} - -/** - * - */ -INLINE PN_stdfloat BulletManifoldPoint:: -get_combined_restitution() const { - - return (PN_stdfloat)_pt.m_combinedRestitution; -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_applied_impulse(PN_stdfloat value) { - - _pt.m_appliedImpulse = (btScalar)value; -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_applied_impulse_lateral1(PN_stdfloat value) { - - _pt.m_appliedImpulseLateral1 = (btScalar)value; -} - -/** - * - */ -INLINE PN_stdfloat BulletManifoldPoint:: -get_applied_impulse_lateral1() const { - - return (PN_stdfloat)_pt.m_appliedImpulseLateral1; -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_applied_impulse_lateral2(PN_stdfloat value) { - - _pt.m_appliedImpulseLateral2 = (btScalar)value; -} - -/** - * - */ -INLINE PN_stdfloat BulletManifoldPoint:: -get_applied_impulse_lateral2() const { - - return (PN_stdfloat)_pt.m_appliedImpulseLateral2; -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_contact_cfm1(PN_stdfloat value) { -#if BT_BULLET_VERSION < 285 - _pt.m_contactCFM1 = (btScalar)value; -#endif -} - -/** - * - */ -INLINE PN_stdfloat BulletManifoldPoint:: -get_contact_cfm1() const { -#if BT_BULLET_VERSION < 285 - return (PN_stdfloat)_pt.m_contactCFM1; -#else - return 0; -#endif -} - -/** - * - */ -INLINE void BulletManifoldPoint:: -set_contact_cfm2(PN_stdfloat value) { -#if BT_BULLET_VERSION < 285 - _pt.m_contactCFM2 = (btScalar)value; -#endif -} - -/** - * - */ -INLINE PN_stdfloat BulletManifoldPoint:: -get_contact_cfm2() const { -#if BT_BULLET_VERSION < 285 - return (PN_stdfloat)_pt.m_contactCFM2; -#else - return 0; -#endif -} diff --git a/panda/src/bullet/bulletManifoldPoint.cxx b/panda/src/bullet/bulletManifoldPoint.cxx index 00651186f0..b56244ed29 100644 --- a/panda/src/bullet/bulletManifoldPoint.cxx +++ b/panda/src/bullet/bulletManifoldPoint.cxx @@ -46,6 +46,7 @@ operator=(const BulletManifoldPoint& other) { */ int BulletManifoldPoint:: get_life_time() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _pt.getLifeTime(); } @@ -55,6 +56,7 @@ get_life_time() const { */ PN_stdfloat BulletManifoldPoint:: get_distance() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_pt.getDistance(); } @@ -64,6 +66,7 @@ get_distance() const { */ PN_stdfloat BulletManifoldPoint:: get_applied_impulse() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_pt.getAppliedImpulse(); } @@ -73,6 +76,7 @@ get_applied_impulse() const { */ LPoint3 BulletManifoldPoint:: get_position_world_on_a() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LPoint3(_pt.getPositionWorldOnA()); } @@ -82,6 +86,7 @@ get_position_world_on_a() const { */ LPoint3 BulletManifoldPoint:: get_position_world_on_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LPoint3(_pt.getPositionWorldOnB()); } @@ -91,6 +96,7 @@ get_position_world_on_b() const { */ LVector3 BulletManifoldPoint:: get_normal_world_on_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_pt.m_normalWorldOnB); } @@ -100,6 +106,7 @@ get_normal_world_on_b() const { */ LPoint3 BulletManifoldPoint:: get_local_point_a() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LPoint3(_pt.m_localPointA); } @@ -109,6 +116,7 @@ get_local_point_a() const { */ LPoint3 BulletManifoldPoint:: get_local_point_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LPoint3(_pt.m_localPointB); } @@ -118,6 +126,7 @@ get_local_point_b() const { */ int BulletManifoldPoint:: get_part_id0() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _pt.m_partId0; } @@ -127,6 +136,7 @@ get_part_id0() const { */ int BulletManifoldPoint:: get_part_id1() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _pt.m_partId1; } @@ -136,6 +146,7 @@ get_part_id1() const { */ int BulletManifoldPoint:: get_index0() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _pt.m_index0; } @@ -145,6 +156,261 @@ get_index0() const { */ int BulletManifoldPoint:: get_index1() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _pt.m_index1; } + +/** + * + */ +void BulletManifoldPoint:: +set_lateral_friction_initialized(bool value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + +#if BT_BULLET_VERSION >= 285 + if (value) { + _pt.m_contactPointFlags |= BT_CONTACT_FLAG_LATERAL_FRICTION_INITIALIZED; + } else { + _pt.m_contactPointFlags &= ~BT_CONTACT_FLAG_LATERAL_FRICTION_INITIALIZED; + } +#else + _pt.m_lateralFrictionInitialized = value; +#endif +} + +/** + * + */ +bool BulletManifoldPoint:: +get_lateral_friction_initialized() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + +#if BT_BULLET_VERSION >= 285 + return (_pt.m_contactPointFlags & BT_CONTACT_FLAG_LATERAL_FRICTION_INITIALIZED) != 0; +#else + return _pt.m_lateralFrictionInitialized; +#endif +} + +/** + * + */ +void BulletManifoldPoint:: +set_lateral_friction_dir1(const LVecBase3 &dir) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_lateralFrictionDir1 = LVecBase3_to_btVector3(dir); +} + +/** + * + */ +LVector3 BulletManifoldPoint:: +get_lateral_friction_dir1() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_pt.m_lateralFrictionDir1); +} + +/** + * + */ +void BulletManifoldPoint:: +set_lateral_friction_dir2(const LVecBase3 &dir) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_lateralFrictionDir2 = LVecBase3_to_btVector3(dir); +} + +/** + * + */ +LVector3 BulletManifoldPoint:: +get_lateral_friction_dir2() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_pt.m_lateralFrictionDir2); +} + +/** + * + */ +void BulletManifoldPoint:: +set_contact_motion1(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_contactMotion1 = (btScalar)value; +} + +/** + * + */ +PN_stdfloat BulletManifoldPoint:: +get_contact_motion1() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_pt.m_contactMotion1; +} + +/** + * + */ +void BulletManifoldPoint:: +set_contact_motion2(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_contactMotion2 = (btScalar)value; +} + +/** + * + */ +PN_stdfloat BulletManifoldPoint:: +get_contact_motion2() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_pt.m_contactMotion2; +} + +/** + * + */ +void BulletManifoldPoint:: +set_combined_friction(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_combinedFriction = (btScalar)value; +} + +/** + * + */ +PN_stdfloat BulletManifoldPoint:: +get_combined_friction() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_pt.m_combinedFriction; +} + +/** + * + */ +void BulletManifoldPoint:: +set_combined_restitution(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_combinedRestitution = (btScalar)value; +} + +/** + * + */ +PN_stdfloat BulletManifoldPoint:: +get_combined_restitution() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_pt.m_combinedRestitution; +} + +/** + * + */ +void BulletManifoldPoint:: +set_applied_impulse(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_appliedImpulse = (btScalar)value; +} + +/** + * + */ +void BulletManifoldPoint:: +set_applied_impulse_lateral1(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_appliedImpulseLateral1 = (btScalar)value; +} + +/** + * + */ +PN_stdfloat BulletManifoldPoint:: +get_applied_impulse_lateral1() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_pt.m_appliedImpulseLateral1; +} + +/** + * + */ +void BulletManifoldPoint:: +set_applied_impulse_lateral2(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _pt.m_appliedImpulseLateral2 = (btScalar)value; +} + +/** + * + */ +PN_stdfloat BulletManifoldPoint:: +get_applied_impulse_lateral2() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_pt.m_appliedImpulseLateral2; +} + +/** + * + */ +void BulletManifoldPoint:: +set_contact_cfm1(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + +#if BT_BULLET_VERSION < 285 + _pt.m_contactCFM1 = (btScalar)value; +#endif +} + +/** + * + */ +PN_stdfloat BulletManifoldPoint:: +get_contact_cfm1() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + +#if BT_BULLET_VERSION < 285 + return (PN_stdfloat)_pt.m_contactCFM1; +#else + return 0; +#endif +} + +/** + * + */ +void BulletManifoldPoint:: +set_contact_cfm2(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + +#if BT_BULLET_VERSION < 285 + _pt.m_contactCFM2 = (btScalar)value; +#endif +} + +/** + * + */ +PN_stdfloat BulletManifoldPoint:: +get_contact_cfm2() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + +#if BT_BULLET_VERSION < 285 + return (PN_stdfloat)_pt.m_contactCFM2; +#else + return 0; +#endif +} diff --git a/panda/src/bullet/bulletManifoldPoint.h b/panda/src/bullet/bulletManifoldPoint.h index a7bc271599..bc4c0f9486 100644 --- a/panda/src/bullet/bulletManifoldPoint.h +++ b/panda/src/bullet/bulletManifoldPoint.h @@ -43,30 +43,30 @@ PUBLISHED: int get_index0() const; int get_index1() const; - INLINE void set_lateral_friction_initialized(bool value); - INLINE void set_lateral_friction_dir1(const LVecBase3 &dir); - INLINE void set_lateral_friction_dir2(const LVecBase3 &dir); - INLINE void set_contact_motion1(PN_stdfloat value); - INLINE void set_contact_motion2(PN_stdfloat value); - INLINE void set_combined_friction(PN_stdfloat value); - INLINE void set_combined_restitution(PN_stdfloat value); - INLINE void set_applied_impulse(PN_stdfloat value); - INLINE void set_applied_impulse_lateral1(PN_stdfloat value); - INLINE void set_applied_impulse_lateral2(PN_stdfloat value); - INLINE void set_contact_cfm1(PN_stdfloat value); - INLINE void set_contact_cfm2(PN_stdfloat value); + void set_lateral_friction_initialized(bool value); + void set_lateral_friction_dir1(const LVecBase3 &dir); + void set_lateral_friction_dir2(const LVecBase3 &dir); + void set_contact_motion1(PN_stdfloat value); + void set_contact_motion2(PN_stdfloat value); + void set_combined_friction(PN_stdfloat value); + void set_combined_restitution(PN_stdfloat value); + void set_applied_impulse(PN_stdfloat value); + void set_applied_impulse_lateral1(PN_stdfloat value); + void set_applied_impulse_lateral2(PN_stdfloat value); + void set_contact_cfm1(PN_stdfloat value); + void set_contact_cfm2(PN_stdfloat value); - INLINE bool get_lateral_friction_initialized() const; - INLINE LVector3 get_lateral_friction_dir1() const; - INLINE LVector3 get_lateral_friction_dir2() const; - INLINE PN_stdfloat get_contact_motion1() const; - INLINE PN_stdfloat get_contact_motion2() const; - INLINE PN_stdfloat get_combined_friction() const; - INLINE PN_stdfloat get_combined_restitution() const; - INLINE PN_stdfloat get_applied_impulse_lateral1() const; - INLINE PN_stdfloat get_applied_impulse_lateral2() const; - INLINE PN_stdfloat get_contact_cfm1() const; - INLINE PN_stdfloat get_contact_cfm2() const; + bool get_lateral_friction_initialized() const; + LVector3 get_lateral_friction_dir1() const; + LVector3 get_lateral_friction_dir2() const; + PN_stdfloat get_contact_motion1() const; + PN_stdfloat get_contact_motion2() const; + PN_stdfloat get_combined_friction() const; + PN_stdfloat get_combined_restitution() const; + PN_stdfloat get_applied_impulse_lateral1() const; + PN_stdfloat get_applied_impulse_lateral2() const; + PN_stdfloat get_contact_cfm1() const; + PN_stdfloat get_contact_cfm2() const; MAKE_PROPERTY(life_time, get_life_time); MAKE_PROPERTY(distance, get_distance); diff --git a/panda/src/bullet/bulletMinkowskiSumShape.I b/panda/src/bullet/bulletMinkowskiSumShape.I index ed4a356f9a..b99d0c7561 100644 --- a/panda/src/bullet/bulletMinkowskiSumShape.I +++ b/panda/src/bullet/bulletMinkowskiSumShape.I @@ -30,64 +30,6 @@ INLINE BulletMinkowskiSumShape:: delete _shape; } -/** - * - */ -INLINE BulletMinkowskiSumShape:: -BulletMinkowskiSumShape(const BulletMinkowskiSumShape ©) : - _shape(copy._shape), - _shape_a(copy._shape_a), - _shape_b(copy._shape_b) { -} - -/** - * - */ -INLINE void BulletMinkowskiSumShape:: -operator = (const BulletMinkowskiSumShape ©) { - _shape = copy._shape; - _shape_a = copy._shape_a; - _shape_b = copy._shape_b; -} - -/** - * - */ -INLINE void BulletMinkowskiSumShape:: -set_transform_a(const TransformState *ts) { - - nassertv(ts); - _shape->setTransformA(TransformState_to_btTrans(ts)); -} - -/** - * - */ -INLINE void BulletMinkowskiSumShape:: -set_transform_b(const TransformState *ts) { - - nassertv(ts); - _shape->setTransformB(TransformState_to_btTrans(ts)); -} - -/** - * - */ -INLINE CPT(TransformState) BulletMinkowskiSumShape:: -get_transform_a() const { - - return btTrans_to_TransformState(_shape->getTransformA()); -} - -/** - * - */ -INLINE CPT(TransformState) BulletMinkowskiSumShape:: -get_transform_b() const { - - return btTrans_to_TransformState(_shape->GetTransformB()); -} - /** * */ diff --git a/panda/src/bullet/bulletMinkowskiSumShape.cxx b/panda/src/bullet/bulletMinkowskiSumShape.cxx index 3dc618adac..cc56d39884 100644 --- a/panda/src/bullet/bulletMinkowskiSumShape.cxx +++ b/panda/src/bullet/bulletMinkowskiSumShape.cxx @@ -33,6 +33,30 @@ BulletMinkowskiSumShape(const BulletShape *shape_a, const BulletShape *shape_b) _shape->setUserPointer(this); } +/** + * + */ +BulletMinkowskiSumShape:: +BulletMinkowskiSumShape(const BulletMinkowskiSumShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _shape_a = copy._shape_a; + _shape_b = copy._shape_b; +} + +/** + * + */ +void BulletMinkowskiSumShape:: +operator = (const BulletMinkowskiSumShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _shape_a = copy._shape_a; + _shape_b = copy._shape_b; +} + /** * */ @@ -42,6 +66,48 @@ ptr() const { return _shape; } +/** + * + */ +void BulletMinkowskiSumShape:: +set_transform_a(const TransformState *ts) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(ts); + _shape->setTransformA(TransformState_to_btTrans(ts)); +} + +/** + * + */ +void BulletMinkowskiSumShape:: +set_transform_b(const TransformState *ts) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(ts); + _shape->setTransformB(TransformState_to_btTrans(ts)); +} + +/** + * + */ +CPT(TransformState) BulletMinkowskiSumShape:: +get_transform_a() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_shape->getTransformA()); +} + +/** + * + */ +CPT(TransformState) BulletMinkowskiSumShape:: +get_transform_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_shape->GetTransformB()); +} + /** * Tells the BamReader how to create objects of type BulletShape. */ diff --git a/panda/src/bullet/bulletMinkowskiSumShape.h b/panda/src/bullet/bulletMinkowskiSumShape.h index 2e66ab9cae..c8629f2e33 100644 --- a/panda/src/bullet/bulletMinkowskiSumShape.h +++ b/panda/src/bullet/bulletMinkowskiSumShape.h @@ -32,14 +32,14 @@ private: PUBLISHED: explicit BulletMinkowskiSumShape(const BulletShape *shape_a, const BulletShape *shape_b); - INLINE BulletMinkowskiSumShape(const BulletMinkowskiSumShape ©); - INLINE void operator = (const BulletMinkowskiSumShape ©); + BulletMinkowskiSumShape(const BulletMinkowskiSumShape ©); + void operator = (const BulletMinkowskiSumShape ©); INLINE ~BulletMinkowskiSumShape(); - INLINE void set_transform_a(const TransformState *ts); - INLINE void set_transform_b(const TransformState *ts); - INLINE CPT(TransformState) get_transform_a() const; - INLINE CPT(TransformState) get_transform_b() const; + void set_transform_a(const TransformState *ts); + void set_transform_b(const TransformState *ts); + CPT(TransformState) get_transform_a() const; + CPT(TransformState) get_transform_b() const; INLINE const BulletShape *get_shape_a() const; INLINE const BulletShape *get_shape_b() const; diff --git a/panda/src/bullet/bulletMultiSphereShape.I b/panda/src/bullet/bulletMultiSphereShape.I index 1024dce7dc..e4f95c7bf1 100644 --- a/panda/src/bullet/bulletMultiSphereShape.I +++ b/panda/src/bullet/bulletMultiSphereShape.I @@ -19,48 +19,3 @@ INLINE BulletMultiSphereShape:: delete _shape; } - -/** - * - */ -INLINE BulletMultiSphereShape:: -BulletMultiSphereShape(const BulletMultiSphereShape ©) : - _shape(copy._shape) { -} - -/** - * - */ -INLINE void BulletMultiSphereShape:: -operator = (const BulletMultiSphereShape ©) { - _shape = copy._shape; -} - -/** - * - */ -INLINE int BulletMultiSphereShape:: -get_sphere_count() const { - - return _shape->getSphereCount(); -} - -/** - * - */ -INLINE LPoint3 BulletMultiSphereShape:: -get_sphere_pos(int index) const { - - nassertr(index >=0 && index <_shape->getSphereCount(), LPoint3::zero()); - return btVector3_to_LPoint3(_shape->getSpherePosition(index)); -} - -/** - * - */ -INLINE PN_stdfloat BulletMultiSphereShape:: -get_sphere_radius(int index) const { - - nassertr(index >=0 && index <_shape->getSphereCount(), 0.0); - return (PN_stdfloat)_shape->getSphereRadius(index); -} diff --git a/panda/src/bullet/bulletMultiSphereShape.cxx b/panda/src/bullet/bulletMultiSphereShape.cxx index 9defaedc51..ffaed8bafb 100644 --- a/panda/src/bullet/bulletMultiSphereShape.cxx +++ b/panda/src/bullet/bulletMultiSphereShape.cxx @@ -42,6 +42,26 @@ BulletMultiSphereShape(const PTA_LVecBase3 &points, const PTA_stdfloat &radii) { _shape->setUserPointer(this); } +/** + * + */ +BulletMultiSphereShape:: +BulletMultiSphereShape(const BulletMultiSphereShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; +} + +/** + * + */ +void BulletMultiSphereShape:: +operator = (const BulletMultiSphereShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; +} + /** * */ @@ -51,6 +71,38 @@ ptr() const { return _shape; } +/** + * + */ +int BulletMultiSphereShape:: +get_sphere_count() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _shape->getSphereCount(); +} + +/** + * + */ +LPoint3 BulletMultiSphereShape:: +get_sphere_pos(int index) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertr(index >=0 && index <_shape->getSphereCount(), LPoint3::zero()); + return btVector3_to_LPoint3(_shape->getSpherePosition(index)); +} + +/** + * + */ +PN_stdfloat BulletMultiSphereShape:: +get_sphere_radius(int index) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertr(index >=0 && index <_shape->getSphereCount(), 0.0); + return (PN_stdfloat)_shape->getSphereRadius(index); +} + /** * Tells the BamReader how to create objects of type BulletShape. */ diff --git a/panda/src/bullet/bulletMultiSphereShape.h b/panda/src/bullet/bulletMultiSphereShape.h index 11d3ee936f..d6fd7af7b7 100644 --- a/panda/src/bullet/bulletMultiSphereShape.h +++ b/panda/src/bullet/bulletMultiSphereShape.h @@ -31,13 +31,13 @@ private: PUBLISHED: explicit BulletMultiSphereShape(const PTA_LVecBase3 &points, const PTA_stdfloat &radii); - INLINE BulletMultiSphereShape(const BulletMultiSphereShape ©); - INLINE void operator = (const BulletMultiSphereShape ©); + BulletMultiSphereShape(const BulletMultiSphereShape ©); + void operator = (const BulletMultiSphereShape ©); INLINE ~BulletMultiSphereShape(); - INLINE int get_sphere_count() const; - INLINE LPoint3 get_sphere_pos(int index) const; - INLINE PN_stdfloat get_sphere_radius(int index) const; + int get_sphere_count() const; + LPoint3 get_sphere_pos(int index) const; + PN_stdfloat get_sphere_radius(int index) const; MAKE_PROPERTY(sphere_count, get_sphere_count); MAKE_SEQ_PROPERTY(sphere_pos, get_sphere_count, get_sphere_pos); diff --git a/panda/src/bullet/bulletPersistentManifold.cxx b/panda/src/bullet/bulletPersistentManifold.cxx index 3a4024f8d2..e42ea3c6f8 100644 --- a/panda/src/bullet/bulletPersistentManifold.cxx +++ b/panda/src/bullet/bulletPersistentManifold.cxx @@ -27,6 +27,7 @@ BulletPersistentManifold(btPersistentManifold *manifold) : _manifold(manifold) { */ PN_stdfloat BulletPersistentManifold:: get_contact_breaking_threshold() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_manifold->getContactBreakingThreshold(); } @@ -36,6 +37,7 @@ get_contact_breaking_threshold() const { */ PN_stdfloat BulletPersistentManifold:: get_contact_processing_threshold() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_manifold->getContactProcessingThreshold(); } @@ -45,6 +47,7 @@ get_contact_processing_threshold() const { */ void BulletPersistentManifold:: clear_manifold() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _manifold->clearManifold(); } @@ -54,6 +57,7 @@ clear_manifold() { */ PandaNode *BulletPersistentManifold:: get_node0() { + LightMutexHolder holder(BulletWorld::get_global_lock()); #if BT_BULLET_VERSION >= 281 const btCollisionObject *obj = _manifold->getBody0(); @@ -69,6 +73,7 @@ get_node0() { */ PandaNode *BulletPersistentManifold:: get_node1() { + LightMutexHolder holder(BulletWorld::get_global_lock()); #if BT_BULLET_VERSION >= 281 const btCollisionObject *obj = _manifold->getBody1(); @@ -84,6 +89,7 @@ get_node1() { */ int BulletPersistentManifold:: get_num_manifold_points() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _manifold->getNumContacts(); } @@ -93,6 +99,7 @@ get_num_manifold_points() const { */ BulletManifoldPoint *BulletPersistentManifold:: get_manifold_point(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertr(idx < _manifold->getNumContacts(), NULL) diff --git a/panda/src/bullet/bulletPlaneShape.I b/panda/src/bullet/bulletPlaneShape.I index f6a4c1e752..0b080ab986 100644 --- a/panda/src/bullet/bulletPlaneShape.I +++ b/panda/src/bullet/bulletPlaneShape.I @@ -19,37 +19,3 @@ INLINE BulletPlaneShape:: delete _shape; } - -/** - * - */ -INLINE BulletPlaneShape:: -BulletPlaneShape(const BulletPlaneShape ©) : - _shape(copy._shape) { -} - -/** - * - */ -INLINE void BulletPlaneShape:: -operator = (const BulletPlaneShape ©) { - _shape = copy._shape; -} - -/** - * - */ -INLINE PN_stdfloat BulletPlaneShape:: -get_plane_constant() const { - - return (PN_stdfloat)_shape->getPlaneConstant(); -} - -/** - * - */ -INLINE LVector3 BulletPlaneShape:: -get_plane_normal() const { - - return btVector3_to_LVector3(_shape->getPlaneNormal()); -} diff --git a/panda/src/bullet/bulletPlaneShape.cxx b/panda/src/bullet/bulletPlaneShape.cxx index 2b410fb0e9..a7804290cd 100644 --- a/panda/src/bullet/bulletPlaneShape.cxx +++ b/panda/src/bullet/bulletPlaneShape.cxx @@ -27,6 +27,26 @@ BulletPlaneShape(const LVector3 &normal, PN_stdfloat constant) { _shape->setUserPointer(this); } +/** + * + */ +BulletPlaneShape:: +BulletPlaneShape(const BulletPlaneShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; +} + +/** + * + */ +void BulletPlaneShape:: +operator = (const BulletPlaneShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; +} + /** * */ @@ -36,6 +56,26 @@ ptr() const { return _shape; } +/** + * + */ +PN_stdfloat BulletPlaneShape:: +get_plane_constant() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_shape->getPlaneConstant(); +} + +/** + * + */ +LVector3 BulletPlaneShape:: +get_plane_normal() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_shape->getPlaneNormal()); +} + /** * */ diff --git a/panda/src/bullet/bulletPlaneShape.h b/panda/src/bullet/bulletPlaneShape.h index 4455328f10..610c5d2412 100644 --- a/panda/src/bullet/bulletPlaneShape.h +++ b/panda/src/bullet/bulletPlaneShape.h @@ -33,12 +33,12 @@ private: PUBLISHED: explicit BulletPlaneShape(const LVector3 &normal, PN_stdfloat constant); - INLINE BulletPlaneShape(const BulletPlaneShape ©); - INLINE void operator = (const BulletPlaneShape ©); + BulletPlaneShape(const BulletPlaneShape ©); + void operator = (const BulletPlaneShape ©); INLINE ~BulletPlaneShape(); - INLINE LVector3 get_plane_normal() const; - INLINE PN_stdfloat get_plane_constant() const; + LVector3 get_plane_normal() const; + PN_stdfloat get_plane_constant() const; static BulletPlaneShape *make_from_solid(const CollisionPlane *solid); diff --git a/panda/src/bullet/bulletRigidBodyNode.I b/panda/src/bullet/bulletRigidBodyNode.I index de1d5eea05..02c9b5dc6d 100644 --- a/panda/src/bullet/bulletRigidBodyNode.I +++ b/panda/src/bullet/bulletRigidBodyNode.I @@ -20,38 +20,3 @@ INLINE BulletRigidBodyNode:: delete _rigid; } -/** - * - */ -INLINE void BulletRigidBodyNode:: -set_linear_damping(PN_stdfloat value) { - - _rigid->setDamping(value, _rigid->getAngularDamping()); -} - -/** - * - */ -INLINE void BulletRigidBodyNode:: -set_angular_damping(PN_stdfloat value) { - - _rigid->setDamping(_rigid->getLinearDamping(), value); -} - -/** - * - */ -INLINE PN_stdfloat BulletRigidBodyNode:: -get_linear_damping() const { - - return (PN_stdfloat)_rigid->getLinearDamping(); -} - -/** - * - */ -INLINE PN_stdfloat BulletRigidBodyNode:: -get_angular_damping() const { - - return (PN_stdfloat)_rigid->getAngularDamping(); -} diff --git a/panda/src/bullet/bulletRigidBodyNode.cxx b/panda/src/bullet/bulletRigidBodyNode.cxx index b8c921e99d..ab8c4327f9 100644 --- a/panda/src/bullet/bulletRigidBodyNode.cxx +++ b/panda/src/bullet/bulletRigidBodyNode.cxx @@ -48,9 +48,11 @@ BulletRigidBodyNode(const char *name) : BulletBodyNode(name) { */ BulletRigidBodyNode:: BulletRigidBodyNode(const BulletRigidBodyNode ©) : - BulletBodyNode(copy), - _motion(copy._motion) + BulletBodyNode(copy) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motion = copy._motion; _rigid = new btRigidBody(*copy._rigid); _rigid->setUserPointer(this); _rigid->setCollisionShape(_shape); @@ -64,6 +66,7 @@ BulletRigidBodyNode(const BulletRigidBodyNode ©) : */ PandaNode *BulletRigidBodyNode:: make_copy() const { + return new BulletRigidBodyNode(*this); } @@ -72,10 +75,11 @@ make_copy() const { */ void BulletRigidBodyNode:: output(ostream &out) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); - BulletBodyNode::output(out); + BulletBodyNode::do_output(out); - out << " mass=" << get_mass(); + out << " mass=" << do_get_mass(); } /** @@ -93,10 +97,10 @@ get_object() const { * The default implementation does nothing. */ void BulletRigidBodyNode:: -shape_changed() { +do_shape_changed() { - set_mass(get_mass()); - transform_changed(); + do_set_mass(do_get_mass()); + do_transform_changed(); } /** @@ -104,9 +108,10 @@ shape_changed() { * automatically computed from the shape of the body. Setting a value of zero * for mass will make the body static. A value of zero can be considered an * infinite mass. + * Assumes the lock(bullet global lock) is held by the caller */ void BulletRigidBodyNode:: -set_mass(PN_stdfloat mass) { +do_set_mass(PN_stdfloat mass) { btScalar bt_mass = mass; btVector3 bt_inertia(0.0, 0.0, 0.0); @@ -119,12 +124,26 @@ set_mass(PN_stdfloat mass) { _rigid->updateInertiaTensor(); } +/** + * Sets the mass of a rigid body. This also modifies the inertia, which is + * automatically computed from the shape of the body. Setting a value of zero + * for mass will make the body static. A value of zero can be considered an + * infinite mass. + */ +void BulletRigidBodyNode:: +set_mass(PN_stdfloat mass) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + do_set_mass(mass); +} + /** * Returns the total mass of a rigid body. A value of zero means that the * body is staic, i.e. has an infinite mass. + * Assumes the lock(bullet global lock) is held by the caller */ PN_stdfloat BulletRigidBodyNode:: -get_mass() const { +do_get_mass() const { btScalar inv_mass = _rigid->getInvMass(); btScalar mass = (inv_mass == btScalar(0.0)) ? btScalar(0.0) : btScalar(1.0) / inv_mass; @@ -132,11 +151,24 @@ get_mass() const { return mass; } +/** + * Returns the total mass of a rigid body. A value of zero means that the + * body is staic, i.e. has an infinite mass. + */ +PN_stdfloat BulletRigidBodyNode:: +get_mass() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return do_get_mass(); +} + + /** * Returns the inverse mass of a rigid body. */ PN_stdfloat BulletRigidBodyNode:: get_inv_mass() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_rigid->getInvMass(); } @@ -153,6 +185,7 @@ get_inv_mass() const { */ void BulletRigidBodyNode:: set_inertia(const LVecBase3 &inertia) { + LightMutexHolder holder(BulletWorld::get_global_lock()); btVector3 inv_inertia( inertia.get_x() == 0.0 ? btScalar(0.0) : btScalar(1.0 / inertia.get_x()), @@ -171,6 +204,7 @@ set_inertia(const LVecBase3 &inertia) { */ LVector3 BulletRigidBodyNode:: get_inertia() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); btVector3 inv_inertia = _rigid->getInvInertiaDiagLocal(); LVector3 inertia( @@ -187,6 +221,7 @@ get_inertia() const { */ LVector3 BulletRigidBodyNode:: get_inv_inertia_diag_local() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_rigid->getInvInertiaDiagLocal()); } @@ -196,6 +231,7 @@ get_inv_inertia_diag_local() const { */ LMatrix3 BulletRigidBodyNode:: get_inv_inertia_tensor_world() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btMatrix3x3_to_LMatrix3(_rigid->getInvInertiaTensorWorld()); } @@ -205,6 +241,7 @@ get_inv_inertia_tensor_world() const { */ void BulletRigidBodyNode:: apply_force(const LVector3 &force, const LPoint3 &pos) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!force.is_nan()); nassertv_always(!pos.is_nan()); @@ -218,6 +255,7 @@ apply_force(const LVector3 &force, const LPoint3 &pos) { */ void BulletRigidBodyNode:: apply_central_force(const LVector3 &force) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!force.is_nan()); @@ -229,6 +267,7 @@ apply_central_force(const LVector3 &force) { */ void BulletRigidBodyNode:: apply_torque(const LVector3 &torque) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!torque.is_nan()); @@ -240,6 +279,7 @@ apply_torque(const LVector3 &torque) { */ void BulletRigidBodyNode:: apply_torque_impulse(const LVector3 &torque) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!torque.is_nan()); @@ -251,6 +291,7 @@ apply_torque_impulse(const LVector3 &torque) { */ void BulletRigidBodyNode:: apply_impulse(const LVector3 &impulse, const LPoint3 &pos) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!impulse.is_nan()); nassertv_always(!pos.is_nan()); @@ -264,6 +305,7 @@ apply_impulse(const LVector3 &impulse, const LPoint3 &pos) { */ void BulletRigidBodyNode:: apply_central_impulse(const LVector3 &impulse) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!impulse.is_nan()); @@ -271,10 +313,10 @@ apply_central_impulse(const LVector3 &impulse) { } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletRigidBodyNode:: -transform_changed() { +do_transform_changed() { if (_motion.sync_disabled()) return; @@ -289,7 +331,7 @@ transform_changed() { _motion.set_net_transform(ts); // For dynamic or static bodies we directly apply the new transform. - if (!is_kinematic()) { + if (!(get_object()->isKinematicObject())) { btTransform trans = TransformState_to_btTrans(ts); _rigid->setCenterOfMassTransform(trans); } @@ -317,18 +359,31 @@ transform_changed() { * */ void BulletRigidBodyNode:: -sync_p2b() { +transform_changed() { - if (is_kinematic()) { - transform_changed(); + if (_motion.sync_disabled()) return; + + LightMutexHolder holder(BulletWorld::get_global_lock()); + + do_transform_changed(); +} + +/** + * Assumes the lock(bullet global lock) is held by the caller + */ +void BulletRigidBodyNode:: +do_sync_p2b() { + + if (get_object()->isKinematicObject()) { + do_transform_changed(); } } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletRigidBodyNode:: -sync_b2p() { +do_sync_b2p() { _motion.sync_b2p((PandaNode *)this); } @@ -338,6 +393,7 @@ sync_b2p() { */ LVector3 BulletRigidBodyNode:: get_linear_velocity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_rigid->getLinearVelocity()); } @@ -347,6 +403,7 @@ get_linear_velocity() const { */ LVector3 BulletRigidBodyNode:: get_angular_velocity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_rigid->getAngularVelocity()); } @@ -356,6 +413,7 @@ get_angular_velocity() const { */ void BulletRigidBodyNode:: set_linear_velocity(const LVector3 &velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!velocity.is_nan()); @@ -367,17 +425,59 @@ set_linear_velocity(const LVector3 &velocity) { */ void BulletRigidBodyNode:: set_angular_velocity(const LVector3 &velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!velocity.is_nan()); _rigid->setAngularVelocity(LVecBase3_to_btVector3(velocity)); } +/** + * + */ +void BulletRigidBodyNode:: +set_linear_damping(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _rigid->setDamping(value, _rigid->getAngularDamping()); +} + +/** + * + */ +void BulletRigidBodyNode:: +set_angular_damping(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _rigid->setDamping(_rigid->getLinearDamping(), value); +} + +/** + * + */ +PN_stdfloat BulletRigidBodyNode:: +get_linear_damping() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_rigid->getLinearDamping(); +} + +/** + * + */ +PN_stdfloat BulletRigidBodyNode:: +get_angular_damping() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_rigid->getAngularDamping(); +} + /** * */ void BulletRigidBodyNode:: clear_forces() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _rigid->clearForces(); } @@ -387,6 +487,7 @@ clear_forces() { */ PN_stdfloat BulletRigidBodyNode:: get_linear_sleep_threshold() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _rigid->getLinearSleepingThreshold(); } @@ -396,6 +497,7 @@ get_linear_sleep_threshold() const { */ PN_stdfloat BulletRigidBodyNode:: get_angular_sleep_threshold() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _rigid->getAngularSleepingThreshold(); } @@ -405,6 +507,7 @@ get_angular_sleep_threshold() const { */ void BulletRigidBodyNode:: set_linear_sleep_threshold(PN_stdfloat threshold) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _rigid->setSleepingThresholds(threshold, _rigid->getAngularSleepingThreshold()); } @@ -414,6 +517,7 @@ set_linear_sleep_threshold(PN_stdfloat threshold) { */ void BulletRigidBodyNode:: set_angular_sleep_threshold(PN_stdfloat threshold) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _rigid->setSleepingThresholds(_rigid->getLinearSleepingThreshold(), threshold); } @@ -423,6 +527,7 @@ set_angular_sleep_threshold(PN_stdfloat threshold) { */ void BulletRigidBodyNode:: set_gravity(const LVector3 &gravity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv_always(!gravity.is_nan()); @@ -434,6 +539,7 @@ set_gravity(const LVector3 &gravity) { */ LVector3 BulletRigidBodyNode:: get_gravity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_rigid->getGravity()); } @@ -443,6 +549,7 @@ get_gravity() const { */ LVector3 BulletRigidBodyNode:: get_linear_factor() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_rigid->getLinearFactor()); } @@ -452,6 +559,7 @@ get_linear_factor() const { */ LVector3 BulletRigidBodyNode:: get_angular_factor() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_rigid->getAngularFactor()); } @@ -461,6 +569,7 @@ get_angular_factor() const { */ void BulletRigidBodyNode:: set_linear_factor(const LVector3 &factor) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _rigid->setLinearFactor(LVecBase3_to_btVector3(factor)); } @@ -470,6 +579,7 @@ set_linear_factor(const LVector3 &factor) { */ void BulletRigidBodyNode:: set_angular_factor(const LVector3 &factor) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _rigid->setAngularFactor(LVecBase3_to_btVector3(factor)); } @@ -479,6 +589,7 @@ set_angular_factor(const LVector3 &factor) { */ LVector3 BulletRigidBodyNode:: get_total_force() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_rigid->getTotalForce()); } @@ -488,6 +599,7 @@ get_total_force() const { */ LVector3 BulletRigidBodyNode:: get_total_torque() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_rigid->getTotalTorque()); } diff --git a/panda/src/bullet/bulletRigidBodyNode.h b/panda/src/bullet/bulletRigidBodyNode.h index c8afaeb8bb..641fdc9fd9 100644 --- a/panda/src/bullet/bulletRigidBodyNode.h +++ b/panda/src/bullet/bulletRigidBodyNode.h @@ -50,10 +50,10 @@ PUBLISHED: void set_angular_velocity(const LVector3 &velocity); // Damping - INLINE PN_stdfloat get_linear_damping() const; - INLINE PN_stdfloat get_angular_damping() const; - INLINE void set_linear_damping(PN_stdfloat value); - INLINE void set_angular_damping(PN_stdfloat value); + PN_stdfloat get_linear_damping() const; + PN_stdfloat get_angular_damping() const; + void set_linear_damping(PN_stdfloat value); + void set_angular_damping(PN_stdfloat value); // Forces void clear_forces(); @@ -108,14 +108,18 @@ public: virtual void output(ostream &out) const; - void sync_p2b(); - void sync_b2p(); + void do_sync_p2b(); + void do_sync_b2p(); protected: virtual void transform_changed(); private: - virtual void shape_changed(); + virtual void do_shape_changed(); + void do_transform_changed(); + + void do_set_mass(PN_stdfloat mass); + PN_stdfloat do_get_mass() const; // The motion state is used for synchronisation between Bullet and the // Panda3D scene graph. diff --git a/panda/src/bullet/bulletRotationalLimitMotor.I b/panda/src/bullet/bulletRotationalLimitMotor.I index 10ed68435c..35b923a9d3 100644 --- a/panda/src/bullet/bulletRotationalLimitMotor.I +++ b/panda/src/bullet/bulletRotationalLimitMotor.I @@ -14,162 +14,7 @@ /** * */ -INLINE bool BulletRotationalLimitMotor:: -is_limited() const { +INLINE BulletRotationalLimitMotor:: +~BulletRotationalLimitMotor() { - return _motor.isLimited(); -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_motor_enabled(bool enabled) { - - _motor.m_enableMotor = enabled; -} - -/** - * - */ -INLINE bool BulletRotationalLimitMotor:: -get_motor_enabled() const { - - return _motor.m_enableMotor; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_low_limit(PN_stdfloat limit) { - - _motor.m_loLimit = (btScalar)limit; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_high_limit(PN_stdfloat limit) { - - _motor.m_hiLimit = (btScalar)limit; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_target_velocity(PN_stdfloat velocity) { - - _motor.m_targetVelocity = (btScalar)velocity; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_max_motor_force(PN_stdfloat force) { - - _motor.m_maxMotorForce = (btScalar)force; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_max_limit_force(PN_stdfloat force) { - - _motor.m_maxLimitForce = (btScalar)force; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_damping(PN_stdfloat damping) { - - _motor.m_damping = (btScalar)damping; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_softness(PN_stdfloat softness) { - - _motor.m_limitSoftness = (btScalar)softness; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_bounce(PN_stdfloat bounce) { - - _motor.m_bounce = (btScalar)bounce; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_normal_cfm(PN_stdfloat cfm) { - - _motor.m_normalCFM = (btScalar)cfm; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_stop_cfm(PN_stdfloat cfm) { - - _motor.m_stopCFM = (btScalar)cfm; -} - -/** - * - */ -INLINE void BulletRotationalLimitMotor:: -set_stop_erp(PN_stdfloat erp) { - - _motor.m_stopERP = (btScalar)erp; -} - -/** - * Retrieves the current value of angle: 0 = free, 1 = at low limit, 2 = at - * high limit. - */ -INLINE int BulletRotationalLimitMotor:: -get_current_limit() const { - - return _motor.m_currentLimit; -} - -/** - * - */ -INLINE PN_stdfloat BulletRotationalLimitMotor:: -get_current_error() const { - - return (PN_stdfloat)_motor.m_currentLimitError; -} - -/** - * - */ -INLINE PN_stdfloat BulletRotationalLimitMotor:: -get_current_position() const { - - return (PN_stdfloat)_motor.m_currentPosition; -} - -/** - * - */ -INLINE PN_stdfloat BulletRotationalLimitMotor:: -get_accumulated_impulse() const { - - return (PN_stdfloat)_motor.m_accumulatedImpulse; } diff --git a/panda/src/bullet/bulletRotationalLimitMotor.cxx b/panda/src/bullet/bulletRotationalLimitMotor.cxx index ce95a1f8d7..d521b7ad94 100644 --- a/panda/src/bullet/bulletRotationalLimitMotor.cxx +++ b/panda/src/bullet/bulletRotationalLimitMotor.cxx @@ -34,7 +34,179 @@ BulletRotationalLimitMotor(const BulletRotationalLimitMotor ©) /** * */ -BulletRotationalLimitMotor:: -~BulletRotationalLimitMotor() { +bool BulletRotationalLimitMotor:: +is_limited() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + return _motor.isLimited(); +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_motor_enabled(bool enabled) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_enableMotor = enabled; +} + +/** + * + */ +bool BulletRotationalLimitMotor:: +get_motor_enabled() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _motor.m_enableMotor; +} +/** + * + */ +void BulletRotationalLimitMotor:: +set_low_limit(PN_stdfloat limit) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_loLimit = (btScalar)limit; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_high_limit(PN_stdfloat limit) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_hiLimit = (btScalar)limit; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_target_velocity(PN_stdfloat velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_targetVelocity = (btScalar)velocity; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_max_motor_force(PN_stdfloat force) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_maxMotorForce = (btScalar)force; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_max_limit_force(PN_stdfloat force) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_maxLimitForce = (btScalar)force; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_damping(PN_stdfloat damping) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_damping = (btScalar)damping; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_softness(PN_stdfloat softness) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_limitSoftness = (btScalar)softness; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_bounce(PN_stdfloat bounce) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_bounce = (btScalar)bounce; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_normal_cfm(PN_stdfloat cfm) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_normalCFM = (btScalar)cfm; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_stop_cfm(PN_stdfloat cfm) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_stopCFM = (btScalar)cfm; +} + +/** + * + */ +void BulletRotationalLimitMotor:: +set_stop_erp(PN_stdfloat erp) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_stopERP = (btScalar)erp; +} + +/** + * Retrieves the current value of angle: 0 = free, 1 = at low limit, 2 = at + * high limit. + */ +int BulletRotationalLimitMotor:: +get_current_limit() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _motor.m_currentLimit; +} + +/** + * + */ +PN_stdfloat BulletRotationalLimitMotor:: +get_current_error() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_motor.m_currentLimitError; +} + +/** + * + */ +PN_stdfloat BulletRotationalLimitMotor:: +get_current_position() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_motor.m_currentPosition; +} + +/** + * + */ +PN_stdfloat BulletRotationalLimitMotor:: +get_accumulated_impulse() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_motor.m_accumulatedImpulse; } diff --git a/panda/src/bullet/bulletRotationalLimitMotor.h b/panda/src/bullet/bulletRotationalLimitMotor.h index a1cce18ff4..d046cd9171 100644 --- a/panda/src/bullet/bulletRotationalLimitMotor.h +++ b/panda/src/bullet/bulletRotationalLimitMotor.h @@ -28,27 +28,27 @@ class EXPCL_PANDABULLET BulletRotationalLimitMotor { PUBLISHED: BulletRotationalLimitMotor(const BulletRotationalLimitMotor ©); - ~BulletRotationalLimitMotor(); + INLINE ~BulletRotationalLimitMotor(); - INLINE void set_motor_enabled(bool enable); - INLINE void set_low_limit(PN_stdfloat limit); - INLINE void set_high_limit(PN_stdfloat limit); - INLINE void set_target_velocity(PN_stdfloat velocity); - INLINE void set_max_motor_force(PN_stdfloat force); - INLINE void set_max_limit_force(PN_stdfloat force); - INLINE void set_damping(PN_stdfloat damping); - INLINE void set_softness(PN_stdfloat softness); - INLINE void set_bounce(PN_stdfloat bounce); - INLINE void set_normal_cfm(PN_stdfloat cfm); - INLINE void set_stop_cfm(PN_stdfloat cfm); - INLINE void set_stop_erp(PN_stdfloat erp); + void set_motor_enabled(bool enable); + void set_low_limit(PN_stdfloat limit); + void set_high_limit(PN_stdfloat limit); + void set_target_velocity(PN_stdfloat velocity); + void set_max_motor_force(PN_stdfloat force); + void set_max_limit_force(PN_stdfloat force); + void set_damping(PN_stdfloat damping); + void set_softness(PN_stdfloat softness); + void set_bounce(PN_stdfloat bounce); + void set_normal_cfm(PN_stdfloat cfm); + void set_stop_cfm(PN_stdfloat cfm); + void set_stop_erp(PN_stdfloat erp); - INLINE bool is_limited() const; - INLINE bool get_motor_enabled() const; - INLINE int get_current_limit() const; - INLINE PN_stdfloat get_current_error() const; - INLINE PN_stdfloat get_current_position() const; - INLINE PN_stdfloat get_accumulated_impulse() const; + bool is_limited() const; + bool get_motor_enabled() const; + int get_current_limit() const; + PN_stdfloat get_current_error() const; + PN_stdfloat get_current_position() const; + PN_stdfloat get_accumulated_impulse() const; MAKE_PROPERTY(limited, is_limited); MAKE_PROPERTY(motor_enabled, get_motor_enabled, set_motor_enabled); diff --git a/panda/src/bullet/bulletShape.I b/panda/src/bullet/bulletShape.I index 35123a868f..c408ccb95b 100644 --- a/panda/src/bullet/bulletShape.I +++ b/panda/src/bullet/bulletShape.I @@ -18,66 +18,3 @@ INLINE BulletShape:: ~BulletShape() { } - -/** - * - */ -INLINE bool BulletShape:: -is_polyhedral() const { - - return ptr()->isPolyhedral(); -} - -/** - * - */ -INLINE bool BulletShape:: -is_convex() const { - - return ptr()->isConvex(); -} - -/** - * - */ -INLINE bool BulletShape:: -is_convex_2d() const { - - return ptr()->isConvex2d(); -} - -/** - * - */ -INLINE bool BulletShape:: -is_concave() const { - - return ptr()->isConcave(); -} - -/** - * - */ -INLINE bool BulletShape:: -is_infinite() const { - - return ptr()->isInfinite(); -} - -/** - * - */ -INLINE bool BulletShape:: -is_non_moving() const { - - return ptr()->isNonMoving(); -} - -/** - * - */ -INLINE bool BulletShape:: -is_soft_body() const { - - return ptr()->isSoftBody(); -} diff --git a/panda/src/bullet/bulletShape.cxx b/panda/src/bullet/bulletShape.cxx index daf8c1e623..49bd6e4f92 100644 --- a/panda/src/bullet/bulletShape.cxx +++ b/panda/src/bullet/bulletShape.cxx @@ -21,6 +21,7 @@ TypeHandle BulletShape::_type_handle; */ const char *BulletShape:: get_name() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return ptr()->getName(); } @@ -30,6 +31,7 @@ get_name() const { */ PN_stdfloat BulletShape:: get_margin() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return ptr()->getMargin(); } @@ -39,6 +41,7 @@ get_margin() const { */ void BulletShape:: set_margin(PN_stdfloat margin) { + LightMutexHolder holder(BulletWorld::get_global_lock()); ptr()->setMargin(margin); } @@ -48,18 +51,29 @@ set_margin(PN_stdfloat margin) { */ LVecBase3 BulletShape:: get_local_scale() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVecBase3(ptr()->getLocalScaling()); } +/** + * Assumes the lock(bullet global lock) is held by the caller + */ +void BulletShape:: +do_set_local_scale(const LVecBase3 &scale) { + + nassertv(!scale.is_nan()); + ptr()->setLocalScaling(LVecBase3_to_btVector3(scale)); +} + /** * */ void BulletShape:: set_local_scale(const LVecBase3 &scale) { + LightMutexHolder holder(BulletWorld::get_global_lock()); - nassertv(!scale.is_nan()); - ptr()->setLocalScaling(LVecBase3_to_btVector3(scale)); + do_set_local_scale(scale); } /** @@ -67,6 +81,7 @@ set_local_scale(const LVecBase3 &scale) { */ BoundingSphere BulletShape:: get_shape_bounds() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); /* btTransform tr; @@ -87,3 +102,73 @@ cout << "origin " << aabbMin.x() << " " << aabbMin.y() << " " << aabbMin.z() << return bounds; } + +/** + * + */ +bool BulletShape:: +is_polyhedral() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return ptr()->isPolyhedral(); +} + +/** + * + */ +bool BulletShape:: +is_convex() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return ptr()->isConvex(); +} + +/** + * + */ +bool BulletShape:: +is_convex_2d() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return ptr()->isConvex2d(); +} + +/** + * + */ +bool BulletShape:: +is_concave() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return ptr()->isConcave(); +} + +/** + * + */ +bool BulletShape:: +is_infinite() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return ptr()->isInfinite(); +} + +/** + * + */ +bool BulletShape:: +is_non_moving() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return ptr()->isNonMoving(); +} + +/** + * + */ +bool BulletShape:: +is_soft_body() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return ptr()->isSoftBody(); +} diff --git a/panda/src/bullet/bulletShape.h b/panda/src/bullet/bulletShape.h index 8ebeaf9290..e9dd08bf14 100644 --- a/panda/src/bullet/bulletShape.h +++ b/panda/src/bullet/bulletShape.h @@ -31,13 +31,13 @@ protected: PUBLISHED: INLINE virtual ~BulletShape(); - INLINE bool is_polyhedral() const; - INLINE bool is_convex() const; - INLINE bool is_convex_2d() const; - INLINE bool is_concave() const; - INLINE bool is_infinite() const; - INLINE bool is_non_moving() const; - INLINE bool is_soft_body() const; + bool is_polyhedral() const; + bool is_convex() const; + bool is_convex_2d() const; + bool is_concave() const; + bool is_infinite() const; + bool is_non_moving() const; + bool is_soft_body() const; void set_margin(PN_stdfloat margin); const char *get_name() const; @@ -61,6 +61,7 @@ public: virtual btCollisionShape *ptr() const = 0; LVecBase3 get_local_scale() const; void set_local_scale(const LVecBase3 &scale); + void do_set_local_scale(const LVecBase3 &scale); public: static TypeHandle get_class_type() { diff --git a/panda/src/bullet/bulletSliderConstraint.I b/panda/src/bullet/bulletSliderConstraint.I index 4035f1399f..3eca50b143 100644 --- a/panda/src/bullet/bulletSliderConstraint.I +++ b/panda/src/bullet/bulletSliderConstraint.I @@ -19,21 +19,3 @@ INLINE BulletSliderConstraint:: delete _constraint; } - -/** - * - */ -INLINE CPT(TransformState) BulletSliderConstraint:: -get_frame_a() const { - - return btTrans_to_TransformState(_constraint->getFrameOffsetA()); -} - -/** - * - */ -INLINE CPT(TransformState) BulletSliderConstraint:: -get_frame_b() const { - - return btTrans_to_TransformState(_constraint->getFrameOffsetB()); -} diff --git a/panda/src/bullet/bulletSliderConstraint.cxx b/panda/src/bullet/bulletSliderConstraint.cxx index cda213b8c2..c803f509cd 100644 --- a/panda/src/bullet/bulletSliderConstraint.cxx +++ b/panda/src/bullet/bulletSliderConstraint.cxx @@ -65,6 +65,7 @@ ptr() const { */ PN_stdfloat BulletSliderConstraint:: get_lower_linear_limit() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_constraint->getLowerLinLimit(); } @@ -74,6 +75,7 @@ get_lower_linear_limit() const { */ PN_stdfloat BulletSliderConstraint:: get_upper_linear_limit() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_constraint->getUpperLinLimit(); } @@ -83,6 +85,7 @@ get_upper_linear_limit() const { */ PN_stdfloat BulletSliderConstraint:: get_lower_angular_limit() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return rad_2_deg(_constraint->getLowerAngLimit()); } @@ -92,6 +95,7 @@ get_lower_angular_limit() const { */ PN_stdfloat BulletSliderConstraint:: get_upper_angular_limit() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return rad_2_deg(_constraint->getUpperAngLimit()); } @@ -101,6 +105,7 @@ get_upper_angular_limit() const { */ void BulletSliderConstraint:: set_lower_linear_limit(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setLowerLinLimit((btScalar)value); } @@ -110,6 +115,7 @@ set_lower_linear_limit(PN_stdfloat value) { */ void BulletSliderConstraint:: set_upper_linear_limit(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setUpperLinLimit((btScalar)value); } @@ -119,6 +125,7 @@ set_upper_linear_limit(PN_stdfloat value) { */ void BulletSliderConstraint:: set_lower_angular_limit(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setLowerAngLimit((btScalar)deg_2_rad(value)); } @@ -128,6 +135,7 @@ set_lower_angular_limit(PN_stdfloat value) { */ void BulletSliderConstraint:: set_upper_angular_limit(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setUpperAngLimit((btScalar)deg_2_rad(value)); } @@ -137,6 +145,7 @@ set_upper_angular_limit(PN_stdfloat value) { */ PN_stdfloat BulletSliderConstraint:: get_linear_pos() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_constraint->getLinearPos(); } @@ -146,6 +155,7 @@ get_linear_pos() const { */ PN_stdfloat BulletSliderConstraint:: get_angular_pos() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_constraint->getAngularPos(); } @@ -155,6 +165,7 @@ get_angular_pos() const { */ void BulletSliderConstraint:: set_powered_linear_motor(bool on) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setPoweredLinMotor(on); } @@ -164,6 +175,7 @@ set_powered_linear_motor(bool on) { */ void BulletSliderConstraint:: set_target_linear_motor_velocity(PN_stdfloat target_velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setTargetLinMotorVelocity((btScalar)target_velocity); } @@ -173,6 +185,7 @@ set_target_linear_motor_velocity(PN_stdfloat target_velocity) { */ void BulletSliderConstraint:: set_max_linear_motor_force(PN_stdfloat max_force) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMaxLinMotorForce((btScalar)max_force); } @@ -182,6 +195,7 @@ set_max_linear_motor_force(PN_stdfloat max_force) { */ bool BulletSliderConstraint:: get_powered_linear_motor() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _constraint->getPoweredLinMotor(); } @@ -191,6 +205,7 @@ get_powered_linear_motor() const { */ PN_stdfloat BulletSliderConstraint:: get_target_linear_motor_velocity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_constraint->getTargetLinMotorVelocity(); } @@ -200,6 +215,7 @@ get_target_linear_motor_velocity() const { */ PN_stdfloat BulletSliderConstraint:: get_max_linear_motor_force() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_constraint->getMaxLinMotorForce(); } @@ -209,6 +225,7 @@ get_max_linear_motor_force() const { */ void BulletSliderConstraint:: set_powered_angular_motor(bool on) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setPoweredAngMotor(on); } @@ -218,6 +235,7 @@ set_powered_angular_motor(bool on) { */ void BulletSliderConstraint:: set_target_angular_motor_velocity(PN_stdfloat target_velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setTargetAngMotorVelocity((btScalar)target_velocity); } @@ -227,6 +245,7 @@ set_target_angular_motor_velocity(PN_stdfloat target_velocity) { */ void BulletSliderConstraint:: set_max_angular_motor_force(PN_stdfloat max_force) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _constraint->setMaxAngMotorForce((btScalar)max_force); } @@ -236,6 +255,7 @@ set_max_angular_motor_force(PN_stdfloat max_force) { */ bool BulletSliderConstraint:: get_powered_angular_motor() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _constraint->getPoweredAngMotor(); } @@ -245,6 +265,7 @@ get_powered_angular_motor() const { */ PN_stdfloat BulletSliderConstraint:: get_target_angular_motor_velocity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_constraint->getTargetAngMotorVelocity(); } @@ -254,6 +275,7 @@ get_target_angular_motor_velocity() const { */ PN_stdfloat BulletSliderConstraint:: get_max_angular_motor_force() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_constraint->getMaxAngMotorForce(); } @@ -263,9 +285,30 @@ get_max_angular_motor_force() const { */ void BulletSliderConstraint:: set_frames(const TransformState *ts_a, const TransformState *ts_b) { + LightMutexHolder holder(BulletWorld::get_global_lock()); btTransform frame_a = TransformState_to_btTrans(ts_a); btTransform frame_b = TransformState_to_btTrans(ts_b); _constraint->setFrames(frame_a, frame_b); } + +/** + * + */ +CPT(TransformState) BulletSliderConstraint:: +get_frame_a() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_constraint->getFrameOffsetA()); +} + +/** + * + */ +CPT(TransformState) BulletSliderConstraint:: +get_frame_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btTrans_to_TransformState(_constraint->getFrameOffsetB()); +} diff --git a/panda/src/bullet/bulletSliderConstraint.h b/panda/src/bullet/bulletSliderConstraint.h index 931e51ec80..6a670867c5 100644 --- a/panda/src/bullet/bulletSliderConstraint.h +++ b/panda/src/bullet/bulletSliderConstraint.h @@ -70,8 +70,8 @@ PUBLISHED: // Frames void set_frames(const TransformState *ts_a, const TransformState *ts_b); - INLINE CPT(TransformState) get_frame_a() const; - INLINE CPT(TransformState) get_frame_b() const; + CPT(TransformState) get_frame_a() const; + CPT(TransformState) get_frame_b() const; MAKE_PROPERTY(linear_pos, get_linear_pos); MAKE_PROPERTY(angular_pos, get_angular_pos); diff --git a/panda/src/bullet/bulletSoftBodyConfig.I b/panda/src/bullet/bulletSoftBodyConfig.I index eed9499bbb..dfd2b36514 100644 --- a/panda/src/bullet/bulletSoftBodyConfig.I +++ b/panda/src/bullet/bulletSoftBodyConfig.I @@ -18,439 +18,3 @@ INLINE BulletSoftBodyConfig:: ~BulletSoftBodyConfig() { } - -/** - * Getter for property kVCF. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_velocities_correction_factor() const { - - return (PN_stdfloat)_cfg.kVCF; -} - -/** - * Setter for property kVCF. - */ -INLINE void BulletSoftBodyConfig:: -set_velocities_correction_factor(PN_stdfloat value) { - - _cfg.kVCF = (btScalar)value; -} - -/** - * Getter for property kDP. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_damping_coefficient() const { - - return (PN_stdfloat)_cfg.kDP; -} - -/** - * Setter for property kDP. - */ -INLINE void BulletSoftBodyConfig:: -set_damping_coefficient(PN_stdfloat value) { - - _cfg.kDP = (btScalar)value; -} - -/** - * Getter for property kDG. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_drag_coefficient() const { - - return (PN_stdfloat)_cfg.kDG; -} - -/** - * Setter for property kDG. - */ -INLINE void BulletSoftBodyConfig:: -set_drag_coefficient(PN_stdfloat value) { - - _cfg.kDG = (btScalar)value; -} - -/** - * Getter for property kLF. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_lift_coefficient() const { - - return (PN_stdfloat)_cfg.kLF; -} - -/** - * Setter for property kLF. - */ -INLINE void BulletSoftBodyConfig:: -set_lift_coefficient(PN_stdfloat value) { - - _cfg.kLF = (btScalar)value; -} - -/** - * Getter for property kPR. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_pressure_coefficient() const { - - return (PN_stdfloat)_cfg.kPR; -} - -/** - * Setter for property kPR. - */ -INLINE void BulletSoftBodyConfig:: -set_pressure_coefficient(PN_stdfloat value) { - - _cfg.kPR = (btScalar)value; -} - -/** - * Getter for property kVC. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_volume_conservation_coefficient() const { - - return (PN_stdfloat)_cfg.kVC; -} - -/** - * Setter for property kVC. - */ -INLINE void BulletSoftBodyConfig:: -set_volume_conservation_coefficient(PN_stdfloat value) { - - _cfg.kVC = (btScalar)value; -} - -/** - * Getter for property kDF. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_dynamic_friction_coefficient() const { - - return (PN_stdfloat)_cfg.kDF; -} - -/** - * Setter for property kDF. - */ -INLINE void BulletSoftBodyConfig:: -set_dynamic_friction_coefficient(PN_stdfloat value) { - - _cfg.kDF = (btScalar)value; -} - -/** - * Getter for property kMT. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_pose_matching_coefficient() const { - - return (PN_stdfloat)_cfg.kMT; -} - -/** - * Setter for property kMT. - */ -INLINE void BulletSoftBodyConfig:: -set_pose_matching_coefficient(PN_stdfloat value) { - - _cfg.kMT = (btScalar)value; -} - -/** - * Getter for property kCHR. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_rigid_contacts_hardness() const { - - return (PN_stdfloat)_cfg.kCHR; -} - -/** - * Setter for property kCHR. - */ -INLINE void BulletSoftBodyConfig:: -set_rigid_contacts_hardness(PN_stdfloat value) { - - _cfg.kCHR = (btScalar)value; -} - -/** - * Getter for property kKHR. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_kinetic_contacts_hardness() const { - - return (PN_stdfloat)_cfg.kKHR; -} - -/** - * Setter for property kKHR. - */ -INLINE void BulletSoftBodyConfig:: -set_kinetic_contacts_hardness(PN_stdfloat value) { - - _cfg.kKHR = (btScalar)value; -} - -/** - * Getter for property kSHR. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_soft_contacts_hardness() const { - - return (PN_stdfloat)_cfg.kSHR; -} - -/** - * Setter for property kSHR. - */ -INLINE void BulletSoftBodyConfig:: -set_soft_contacts_hardness(PN_stdfloat value) { - - _cfg.kSHR = (btScalar)value; -} - -/** - * Getter for property kAHR. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_anchors_hardness() const { - - return (PN_stdfloat)_cfg.kAHR; -} - -/** - * Setter for property kAHR. - */ -INLINE void BulletSoftBodyConfig:: -set_anchors_hardness(PN_stdfloat value) { - - _cfg.kAHR = (btScalar)value; -} - -/** - * Getter for property kSRHR_CL. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_soft_vs_rigid_hardness() const { - - return (PN_stdfloat)_cfg.kSRHR_CL; -} - -/** - * Setter for property kSRHR_CL. - */ -INLINE void BulletSoftBodyConfig:: -set_soft_vs_rigid_hardness(PN_stdfloat value) { - - _cfg.kSRHR_CL = (btScalar)value; -} - -/** - * Getter for property kSKHR_CL. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_soft_vs_kinetic_hardness() const { - - return (PN_stdfloat)_cfg.kSKHR_CL; -} - -/** - * Setter for property kSKHR_CL. - */ -INLINE void BulletSoftBodyConfig:: -set_soft_vs_kinetic_hardness(PN_stdfloat value) { - - _cfg.kSKHR_CL = (btScalar)value; -} - -/** - * Getter for property kSSHR_CL. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_soft_vs_soft_hardness() const { - - return (PN_stdfloat)_cfg.kSSHR_CL; -} - -/** - * Setter for property kSSHR_CL. - */ -INLINE void BulletSoftBodyConfig:: -set_soft_vs_soft_hardness(PN_stdfloat value) { - - _cfg.kSSHR_CL = (btScalar)value; -} - -/** - * Getter for property kSR_SPLT_CL. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_soft_vs_rigid_impulse_split() const { - - return (PN_stdfloat)_cfg.kSR_SPLT_CL; -} - -/** - * Setter for property kSR_SPLT_CL. - */ -INLINE void BulletSoftBodyConfig:: -set_soft_vs_rigid_impulse_split(PN_stdfloat value) { - - _cfg.kSR_SPLT_CL = (btScalar)value; -} - -/** - * Getter for property kSK_SPLT_CL. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_soft_vs_kinetic_impulse_split() const { - - return (PN_stdfloat)_cfg.kSK_SPLT_CL; -} - -/** - * Setter for property kSK_SPLT_CL. - */ -INLINE void BulletSoftBodyConfig:: -set_soft_vs_kinetic_impulse_split(PN_stdfloat value) { - - _cfg.kSK_SPLT_CL = (btScalar)value; -} - -/** - * Getter for property kSS_SPLT_CL. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_soft_vs_soft_impulse_split() const { - - return (PN_stdfloat)_cfg.kSS_SPLT_CL; -} - -/** - * Setter for property kSS_SPLT_CL. - */ -INLINE void BulletSoftBodyConfig:: -set_soft_vs_soft_impulse_split(PN_stdfloat value) { - - _cfg.kSS_SPLT_CL = (btScalar)value; -} - -/** - * Getter for property maxvolume. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_maxvolume() const { - - return (PN_stdfloat)_cfg.maxvolume; -} - -/** - * Setter for property maxvolume. - */ -INLINE void BulletSoftBodyConfig:: -set_maxvolume(PN_stdfloat value) { - - _cfg.maxvolume = (btScalar)value; -} - -/** - * Getter for property timescale. - */ -INLINE PN_stdfloat BulletSoftBodyConfig:: -get_timescale() const { - - return (PN_stdfloat)_cfg.timescale; -} - -/** - * Setter for property timescale. - */ -INLINE void BulletSoftBodyConfig:: -set_timescale(PN_stdfloat value) { - - _cfg.timescale = (btScalar)value; -} - -/** - * Getter for property piterations. - */ -INLINE int BulletSoftBodyConfig:: -get_positions_solver_iterations() const { - - return _cfg.piterations; -} - -/** - * Setter for property piterations. - */ -INLINE void BulletSoftBodyConfig:: -set_positions_solver_iterations(int value) { - - nassertv(value > 0); - _cfg.piterations = value; -} - -/** - * Getter for property viterations. - */ -INLINE int BulletSoftBodyConfig:: -get_velocities_solver_iterations() const { - - return _cfg.viterations; -} - -/** - * Setter for property viterations. - */ -INLINE void BulletSoftBodyConfig:: -set_velocities_solver_iterations(int value) { - - nassertv(value > 0); - _cfg.viterations = value; -} - -/** - * Getter for property diterations. - */ -INLINE int BulletSoftBodyConfig:: -get_drift_solver_iterations() const { - - return _cfg.diterations; -} - -/** - * Setter for property diterations. - */ -INLINE void BulletSoftBodyConfig:: -set_drift_solver_iterations(int value) { - - nassertv(value > 0); - _cfg.diterations = value; -} - -/** - * Getter for property citerations. - */ -INLINE int BulletSoftBodyConfig:: -get_cluster_solver_iterations() const { - - return _cfg.citerations; -} - -/** - * Setter for property citerations. - */ -INLINE void BulletSoftBodyConfig:: -set_cluster_solver_iterations(int value) { - - nassertv(value > 0); - _cfg.citerations = value; -} diff --git a/panda/src/bullet/bulletSoftBodyConfig.cxx b/panda/src/bullet/bulletSoftBodyConfig.cxx index dc12fde309..a6db77cb96 100644 --- a/panda/src/bullet/bulletSoftBodyConfig.cxx +++ b/panda/src/bullet/bulletSoftBodyConfig.cxx @@ -26,6 +26,7 @@ BulletSoftBodyConfig(btSoftBody::Config &cfg) : _cfg(cfg) { */ void BulletSoftBodyConfig:: clear_all_collision_flags() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _cfg.collisions = 0; } @@ -35,6 +36,7 @@ clear_all_collision_flags() { */ void BulletSoftBodyConfig:: set_collision_flag(CollisionFlag flag, bool value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); if (value == true) { _cfg.collisions |= flag; @@ -49,6 +51,7 @@ set_collision_flag(CollisionFlag flag, bool value) { */ bool BulletSoftBodyConfig:: get_collision_flag(CollisionFlag flag) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (_cfg.collisions & flag) ? true : false; } @@ -58,6 +61,7 @@ get_collision_flag(CollisionFlag flag) const { */ void BulletSoftBodyConfig:: set_aero_model(AeroModel value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _cfg.aeromodel = (btSoftBody::eAeroModel::_)value; } @@ -67,6 +71,491 @@ set_aero_model(AeroModel value) { */ BulletSoftBodyConfig::AeroModel BulletSoftBodyConfig:: get_aero_model() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (AeroModel)_cfg.aeromodel; } + +/** + * Getter for property kVCF. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_velocities_correction_factor() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kVCF; +} + +/** + * Setter for property kVCF. + */ +void BulletSoftBodyConfig:: +set_velocities_correction_factor(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kVCF = (btScalar)value; +} + +/** + * Getter for property kDP. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_damping_coefficient() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kDP; +} + +/** + * Setter for property kDP. + */ +void BulletSoftBodyConfig:: +set_damping_coefficient(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kDP = (btScalar)value; +} + +/** + * Getter for property kDG. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_drag_coefficient() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kDG; +} + +/** + * Setter for property kDG. + */ +void BulletSoftBodyConfig:: +set_drag_coefficient(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kDG = (btScalar)value; +} + +/** + * Getter for property kLF. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_lift_coefficient() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kLF; +} + +/** + * Setter for property kLF. + */ +void BulletSoftBodyConfig:: +set_lift_coefficient(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kLF = (btScalar)value; +} + +/** + * Getter for property kPR. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_pressure_coefficient() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kPR; +} + +/** + * Setter for property kPR. + */ +void BulletSoftBodyConfig:: +set_pressure_coefficient(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kPR = (btScalar)value; +} + +/** + * Getter for property kVC. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_volume_conservation_coefficient() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kVC; +} + +/** + * Setter for property kVC. + */ +void BulletSoftBodyConfig:: +set_volume_conservation_coefficient(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kVC = (btScalar)value; +} + +/** + * Getter for property kDF. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_dynamic_friction_coefficient() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kDF; +} + +/** + * Setter for property kDF. + */ +void BulletSoftBodyConfig:: +set_dynamic_friction_coefficient(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kDF = (btScalar)value; +} + +/** + * Getter for property kMT. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_pose_matching_coefficient() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kMT; +} + +/** + * Setter for property kMT. + */ +void BulletSoftBodyConfig:: +set_pose_matching_coefficient(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kMT = (btScalar)value; +} + +/** + * Getter for property kCHR. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_rigid_contacts_hardness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kCHR; +} + +/** + * Setter for property kCHR. + */ +void BulletSoftBodyConfig:: +set_rigid_contacts_hardness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kCHR = (btScalar)value; +} + +/** + * Getter for property kKHR. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_kinetic_contacts_hardness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kKHR; +} + +/** + * Setter for property kKHR. + */ +void BulletSoftBodyConfig:: +set_kinetic_contacts_hardness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kKHR = (btScalar)value; +} + +/** + * Getter for property kSHR. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_soft_contacts_hardness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kSHR; +} + +/** + * Setter for property kSHR. + */ +void BulletSoftBodyConfig:: +set_soft_contacts_hardness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kSHR = (btScalar)value; +} + +/** + * Getter for property kAHR. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_anchors_hardness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kAHR; +} + +/** + * Setter for property kAHR. + */ +void BulletSoftBodyConfig:: +set_anchors_hardness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kAHR = (btScalar)value; +} + +/** + * Getter for property kSRHR_CL. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_soft_vs_rigid_hardness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kSRHR_CL; +} + +/** + * Setter for property kSRHR_CL. + */ +void BulletSoftBodyConfig:: +set_soft_vs_rigid_hardness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kSRHR_CL = (btScalar)value; +} + +/** + * Getter for property kSKHR_CL. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_soft_vs_kinetic_hardness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kSKHR_CL; +} + +/** + * Setter for property kSKHR_CL. + */ +void BulletSoftBodyConfig:: +set_soft_vs_kinetic_hardness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kSKHR_CL = (btScalar)value; +} + +/** + * Getter for property kSSHR_CL. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_soft_vs_soft_hardness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kSSHR_CL; +} + +/** + * Setter for property kSSHR_CL. + */ +void BulletSoftBodyConfig:: +set_soft_vs_soft_hardness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kSSHR_CL = (btScalar)value; +} + +/** + * Getter for property kSR_SPLT_CL. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_soft_vs_rigid_impulse_split() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kSR_SPLT_CL; +} + +/** + * Setter for property kSR_SPLT_CL. + */ +void BulletSoftBodyConfig:: +set_soft_vs_rigid_impulse_split(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kSR_SPLT_CL = (btScalar)value; +} + +/** + * Getter for property kSK_SPLT_CL. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_soft_vs_kinetic_impulse_split() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kSK_SPLT_CL; +} + +/** + * Setter for property kSK_SPLT_CL. + */ +void BulletSoftBodyConfig:: +set_soft_vs_kinetic_impulse_split(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kSK_SPLT_CL = (btScalar)value; +} + +/** + * Getter for property kSS_SPLT_CL. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_soft_vs_soft_impulse_split() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.kSS_SPLT_CL; +} + +/** + * Setter for property kSS_SPLT_CL. + */ +void BulletSoftBodyConfig:: +set_soft_vs_soft_impulse_split(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.kSS_SPLT_CL = (btScalar)value; +} + +/** + * Getter for property maxvolume. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_maxvolume() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.maxvolume; +} + +/** + * Setter for property maxvolume. + */ +void BulletSoftBodyConfig:: +set_maxvolume(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.maxvolume = (btScalar)value; +} + +/** + * Getter for property timescale. + */ +PN_stdfloat BulletSoftBodyConfig:: +get_timescale() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_cfg.timescale; +} + +/** + * Setter for property timescale. + */ +void BulletSoftBodyConfig:: +set_timescale(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _cfg.timescale = (btScalar)value; +} + +/** + * Getter for property piterations. + */ +int BulletSoftBodyConfig:: +get_positions_solver_iterations() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _cfg.piterations; +} + +/** + * Setter for property piterations. + */ +void BulletSoftBodyConfig:: +set_positions_solver_iterations(int value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(value > 0); + _cfg.piterations = value; +} + +/** + * Getter for property viterations. + */ +int BulletSoftBodyConfig:: +get_velocities_solver_iterations() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _cfg.viterations; +} + +/** + * Setter for property viterations. + */ +void BulletSoftBodyConfig:: +set_velocities_solver_iterations(int value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(value > 0); + _cfg.viterations = value; +} + +/** + * Getter for property diterations. + */ +int BulletSoftBodyConfig:: +get_drift_solver_iterations() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _cfg.diterations; +} + +/** + * Setter for property diterations. + */ +void BulletSoftBodyConfig:: +set_drift_solver_iterations(int value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(value > 0); + _cfg.diterations = value; +} + +/** + * Getter for property citerations. + */ +int BulletSoftBodyConfig:: +get_cluster_solver_iterations() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _cfg.citerations; +} + +/** + * Setter for property citerations. + */ +void BulletSoftBodyConfig:: +set_cluster_solver_iterations(int value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(value > 0); + _cfg.citerations = value; +} diff --git a/panda/src/bullet/bulletSoftBodyConfig.h b/panda/src/bullet/bulletSoftBodyConfig.h index 2411797146..18bbd49dfa 100644 --- a/panda/src/bullet/bulletSoftBodyConfig.h +++ b/panda/src/bullet/bulletSoftBodyConfig.h @@ -51,55 +51,55 @@ PUBLISHED: void set_aero_model(AeroModel value); AeroModel get_aero_model() const; - INLINE void set_velocities_correction_factor(PN_stdfloat value); - INLINE void set_damping_coefficient(PN_stdfloat value); - INLINE void set_drag_coefficient(PN_stdfloat value); - INLINE void set_lift_coefficient(PN_stdfloat value); - INLINE void set_pressure_coefficient(PN_stdfloat value); - INLINE void set_volume_conservation_coefficient(PN_stdfloat value); - INLINE void set_dynamic_friction_coefficient(PN_stdfloat value); - INLINE void set_pose_matching_coefficient(PN_stdfloat value); - INLINE void set_rigid_contacts_hardness(PN_stdfloat value); - INLINE void set_kinetic_contacts_hardness(PN_stdfloat value); - INLINE void set_soft_contacts_hardness(PN_stdfloat value); - INLINE void set_anchors_hardness(PN_stdfloat value); - INLINE void set_soft_vs_rigid_hardness(PN_stdfloat value); - INLINE void set_soft_vs_kinetic_hardness(PN_stdfloat value); - INLINE void set_soft_vs_soft_hardness(PN_stdfloat value); - INLINE void set_soft_vs_rigid_impulse_split(PN_stdfloat value); - INLINE void set_soft_vs_kinetic_impulse_split(PN_stdfloat value); - INLINE void set_soft_vs_soft_impulse_split(PN_stdfloat value); - INLINE void set_maxvolume(PN_stdfloat value); - INLINE void set_timescale(PN_stdfloat value); - INLINE void set_positions_solver_iterations(int value); - INLINE void set_velocities_solver_iterations(int value); - INLINE void set_drift_solver_iterations( int value); - INLINE void set_cluster_solver_iterations(int value); + void set_velocities_correction_factor(PN_stdfloat value); + void set_damping_coefficient(PN_stdfloat value); + void set_drag_coefficient(PN_stdfloat value); + void set_lift_coefficient(PN_stdfloat value); + void set_pressure_coefficient(PN_stdfloat value); + void set_volume_conservation_coefficient(PN_stdfloat value); + void set_dynamic_friction_coefficient(PN_stdfloat value); + void set_pose_matching_coefficient(PN_stdfloat value); + void set_rigid_contacts_hardness(PN_stdfloat value); + void set_kinetic_contacts_hardness(PN_stdfloat value); + void set_soft_contacts_hardness(PN_stdfloat value); + void set_anchors_hardness(PN_stdfloat value); + void set_soft_vs_rigid_hardness(PN_stdfloat value); + void set_soft_vs_kinetic_hardness(PN_stdfloat value); + void set_soft_vs_soft_hardness(PN_stdfloat value); + void set_soft_vs_rigid_impulse_split(PN_stdfloat value); + void set_soft_vs_kinetic_impulse_split(PN_stdfloat value); + void set_soft_vs_soft_impulse_split(PN_stdfloat value); + void set_maxvolume(PN_stdfloat value); + void set_timescale(PN_stdfloat value); + void set_positions_solver_iterations(int value); + void set_velocities_solver_iterations(int value); + void set_drift_solver_iterations( int value); + void set_cluster_solver_iterations(int value); - INLINE PN_stdfloat get_velocities_correction_factor() const; - INLINE PN_stdfloat get_damping_coefficient() const; - INLINE PN_stdfloat get_drag_coefficient() const; - INLINE PN_stdfloat get_lift_coefficient() const; - INLINE PN_stdfloat get_pressure_coefficient() const; - INLINE PN_stdfloat get_volume_conservation_coefficient() const; - INLINE PN_stdfloat get_dynamic_friction_coefficient() const; - INLINE PN_stdfloat get_pose_matching_coefficient() const; - INLINE PN_stdfloat get_rigid_contacts_hardness() const; - INLINE PN_stdfloat get_kinetic_contacts_hardness() const; - INLINE PN_stdfloat get_soft_contacts_hardness() const; - INLINE PN_stdfloat get_anchors_hardness() const; - INLINE PN_stdfloat get_soft_vs_rigid_hardness() const; - INLINE PN_stdfloat get_soft_vs_kinetic_hardness() const; - INLINE PN_stdfloat get_soft_vs_soft_hardness() const; - INLINE PN_stdfloat get_soft_vs_rigid_impulse_split() const; - INLINE PN_stdfloat get_soft_vs_kinetic_impulse_split() const; - INLINE PN_stdfloat get_soft_vs_soft_impulse_split() const; - INLINE PN_stdfloat get_maxvolume() const; - INLINE PN_stdfloat get_timescale() const; - INLINE int get_positions_solver_iterations() const; - INLINE int get_velocities_solver_iterations() const; - INLINE int get_drift_solver_iterations() const; - INLINE int get_cluster_solver_iterations() const; + PN_stdfloat get_velocities_correction_factor() const; + PN_stdfloat get_damping_coefficient() const; + PN_stdfloat get_drag_coefficient() const; + PN_stdfloat get_lift_coefficient() const; + PN_stdfloat get_pressure_coefficient() const; + PN_stdfloat get_volume_conservation_coefficient() const; + PN_stdfloat get_dynamic_friction_coefficient() const; + PN_stdfloat get_pose_matching_coefficient() const; + PN_stdfloat get_rigid_contacts_hardness() const; + PN_stdfloat get_kinetic_contacts_hardness() const; + PN_stdfloat get_soft_contacts_hardness() const; + PN_stdfloat get_anchors_hardness() const; + PN_stdfloat get_soft_vs_rigid_hardness() const; + PN_stdfloat get_soft_vs_kinetic_hardness() const; + PN_stdfloat get_soft_vs_soft_hardness() const; + PN_stdfloat get_soft_vs_rigid_impulse_split() const; + PN_stdfloat get_soft_vs_kinetic_impulse_split() const; + PN_stdfloat get_soft_vs_soft_impulse_split() const; + PN_stdfloat get_maxvolume() const; + PN_stdfloat get_timescale() const; + int get_positions_solver_iterations() const; + int get_velocities_solver_iterations() const; + int get_drift_solver_iterations() const; + int get_cluster_solver_iterations() const; MAKE_PROPERTY(aero_model, get_aero_model, set_aero_model); MAKE_PROPERTY(velocities_correction_factor, get_velocities_correction_factor, set_velocities_correction_factor); diff --git a/panda/src/bullet/bulletSoftBodyMaterial.I b/panda/src/bullet/bulletSoftBodyMaterial.I index ff8182f6ed..7514fc7cbf 100644 --- a/panda/src/bullet/bulletSoftBodyMaterial.I +++ b/panda/src/bullet/bulletSoftBodyMaterial.I @@ -30,66 +30,3 @@ empty() { return BulletSoftBodyMaterial(material); } - -/** - * - */ -INLINE btSoftBody::Material &BulletSoftBodyMaterial:: -get_material() const { - - return _material; -} - -/** - * Getter for the property m_kLST. - */ -INLINE PN_stdfloat BulletSoftBodyMaterial:: -get_linear_stiffness() const { - - return (PN_stdfloat)_material.m_kLST; -} - -/** - * Setter for the property m_kLST. - */ -INLINE void BulletSoftBodyMaterial:: -set_linear_stiffness(PN_stdfloat value) { - - _material.m_kLST = (btScalar)value; -} - -/** - * Getter for the property m_kAST. - */ -INLINE PN_stdfloat BulletSoftBodyMaterial:: -get_angular_stiffness() const { - - return (PN_stdfloat)_material.m_kAST; -} - -/** - * Setter for the property m_kAST. - */ -INLINE void BulletSoftBodyMaterial:: -set_angular_stiffness(PN_stdfloat value) { - - _material.m_kAST = (btScalar)value; -} - -/** - * Getter for the property m_kVST. - */ -INLINE PN_stdfloat BulletSoftBodyMaterial:: -get_volume_preservation() const { - - return (PN_stdfloat)_material.m_kVST; -} - -/** - * Setter for the property m_kVST. - */ -INLINE void BulletSoftBodyMaterial:: -set_volume_preservation(PN_stdfloat value) { - - _material.m_kVST = (btScalar)value; -} diff --git a/panda/src/bullet/bulletSoftBodyMaterial.cxx b/panda/src/bullet/bulletSoftBodyMaterial.cxx index 1767513a27..d40a7bd984 100644 --- a/panda/src/bullet/bulletSoftBodyMaterial.cxx +++ b/panda/src/bullet/bulletSoftBodyMaterial.cxx @@ -20,3 +20,72 @@ BulletSoftBodyMaterial:: BulletSoftBodyMaterial(btSoftBody::Material &material) : _material(material) { } + +/** + * + */ +btSoftBody::Material &BulletSoftBodyMaterial:: +get_material() const { + + return _material; +} + +/** + * Getter for the property m_kLST. + */ +PN_stdfloat BulletSoftBodyMaterial:: +get_linear_stiffness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_material.m_kLST; +} + +/** + * Setter for the property m_kLST. + */ +void BulletSoftBodyMaterial:: +set_linear_stiffness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _material.m_kLST = (btScalar)value; +} + +/** + * Getter for the property m_kAST. + */ +PN_stdfloat BulletSoftBodyMaterial:: +get_angular_stiffness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_material.m_kAST; +} + +/** + * Setter for the property m_kAST. + */ +void BulletSoftBodyMaterial:: +set_angular_stiffness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _material.m_kAST = (btScalar)value; +} + +/** + * Getter for the property m_kVST. + */ +PN_stdfloat BulletSoftBodyMaterial:: +get_volume_preservation() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_material.m_kVST; +} + +/** + * Setter for the property m_kVST. + */ +void BulletSoftBodyMaterial:: +set_volume_preservation(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _material.m_kVST = (btScalar)value; +} diff --git a/panda/src/bullet/bulletSoftBodyMaterial.h b/panda/src/bullet/bulletSoftBodyMaterial.h index 3f1c07a7aa..bf8c8589a9 100644 --- a/panda/src/bullet/bulletSoftBodyMaterial.h +++ b/panda/src/bullet/bulletSoftBodyMaterial.h @@ -27,14 +27,14 @@ PUBLISHED: INLINE ~BulletSoftBodyMaterial(); INLINE static BulletSoftBodyMaterial empty(); - INLINE PN_stdfloat get_linear_stiffness() const; - INLINE void set_linear_stiffness(PN_stdfloat value); + PN_stdfloat get_linear_stiffness() const; + void set_linear_stiffness(PN_stdfloat value); - INLINE PN_stdfloat get_angular_stiffness() const; - INLINE void set_angular_stiffness(PN_stdfloat value); - - INLINE PN_stdfloat get_volume_preservation() const; - INLINE void set_volume_preservation(PN_stdfloat value); + PN_stdfloat get_angular_stiffness() const; + void set_angular_stiffness(PN_stdfloat value); + + PN_stdfloat get_volume_preservation() const; + void set_volume_preservation(PN_stdfloat value); MAKE_PROPERTY(linear_stiffness, get_linear_stiffness, set_linear_stiffness); MAKE_PROPERTY(angular_stiffness, get_angular_stiffness, set_angular_stiffness); diff --git a/panda/src/bullet/bulletSoftBodyNode.I b/panda/src/bullet/bulletSoftBodyNode.I index 086c3396e7..6a60de43f1 100644 --- a/panda/src/bullet/bulletSoftBodyNode.I +++ b/panda/src/bullet/bulletSoftBodyNode.I @@ -40,56 +40,3 @@ empty() { return BulletSoftBodyNodeElement(node); } -/** - * - */ -INLINE LPoint3 BulletSoftBodyNodeElement:: -get_pos() const { - - return btVector3_to_LPoint3(_node.m_x); -} - -/** - * - */ -INLINE LVector3 BulletSoftBodyNodeElement:: -get_normal() const { - - return btVector3_to_LVector3(_node.m_n); -} - -/** - * - */ -INLINE LVector3 BulletSoftBodyNodeElement:: -get_velocity() const { - - return btVector3_to_LVector3(_node.m_v); -} - -/** - * - */ -INLINE PN_stdfloat BulletSoftBodyNodeElement:: -get_inv_mass() const { - - return (PN_stdfloat)_node.m_im; -} - -/** - * - */ -INLINE PN_stdfloat BulletSoftBodyNodeElement:: -get_area() const { - - return (PN_stdfloat)_node.m_area; -} - -/** - * - */ -INLINE int BulletSoftBodyNodeElement:: -is_attached() const { - - return (PN_stdfloat)_node.m_battach; -} diff --git a/panda/src/bullet/bulletSoftBodyNode.cxx b/panda/src/bullet/bulletSoftBodyNode.cxx index 1d5a70d93f..c4094af652 100644 --- a/panda/src/bullet/bulletSoftBodyNode.cxx +++ b/panda/src/bullet/bulletSoftBodyNode.cxx @@ -66,6 +66,7 @@ get_object() const { */ BulletSoftBodyConfig BulletSoftBodyNode:: get_cfg() { + LightMutexHolder holder(BulletWorld::get_global_lock()); return BulletSoftBodyConfig(_soft->m_cfg); } @@ -75,6 +76,7 @@ get_cfg() { */ BulletSoftBodyWorldInfo BulletSoftBodyNode:: get_world_info() { + LightMutexHolder holder(BulletWorld::get_global_lock()); return BulletSoftBodyWorldInfo(*(_soft->m_worldInfo)); } @@ -84,6 +86,7 @@ get_world_info() { */ int BulletSoftBodyNode:: get_num_materials() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _soft->m_materials.size(); } @@ -93,8 +96,9 @@ get_num_materials() const { */ BulletSoftBodyMaterial BulletSoftBodyNode:: get_material(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); - nassertr(idx >= 0 && idx < get_num_materials(), BulletSoftBodyMaterial::empty()); + nassertr(idx >= 0 && idx < _soft->m_materials.size(), BulletSoftBodyMaterial::empty()); btSoftBody::Material *material = _soft->m_materials[idx]; return BulletSoftBodyMaterial(*material); @@ -105,6 +109,7 @@ get_material(int idx) const { */ BulletSoftBodyMaterial BulletSoftBodyNode:: append_material() { + LightMutexHolder holder(BulletWorld::get_global_lock()); btSoftBody::Material *material = _soft->appendMaterial(); nassertr(material, BulletSoftBodyMaterial::empty()); @@ -117,6 +122,7 @@ append_material() { */ int BulletSoftBodyNode:: get_num_nodes() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _soft->m_nodes.size(); } @@ -126,6 +132,7 @@ get_num_nodes() const { */ BulletSoftBodyNodeElement BulletSoftBodyNode:: get_node(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertr(idx >=0 && idx < get_num_nodes(), BulletSoftBodyNodeElement::empty()); return BulletSoftBodyNodeElement(_soft->m_nodes[idx]); @@ -136,6 +143,7 @@ get_node(int idx) const { */ void BulletSoftBodyNode:: generate_bending_constraints(int distance, BulletSoftBodyMaterial *material) { + LightMutexHolder holder(BulletWorld::get_global_lock()); if (material) { _soft->generateBendingConstraints(distance, &(material->get_material())); @@ -150,6 +158,7 @@ generate_bending_constraints(int distance, BulletSoftBodyMaterial *material) { */ void BulletSoftBodyNode:: randomize_constraints() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->randomizeConstraints(); } @@ -159,9 +168,10 @@ randomize_constraints() { */ void BulletSoftBodyNode:: transform_changed() { - if (_sync_disable) return; + LightMutexHolder holder(BulletWorld::get_global_lock()); + NodePath np = NodePath::any_path((PandaNode *)this); CPT(TransformState) ts = np.get_net_transform(); @@ -174,7 +184,7 @@ transform_changed() { btTransform trans = TransformState_to_btTrans(ts); // Offset between current approx center and current initial transform - btVector3 pos = LVecBase3_to_btVector3(this->get_aabb().get_approx_center()); + btVector3 pos = LVecBase3_to_btVector3(this->do_get_aabb().get_approx_center()); btVector3 origin = _soft->m_initialWorldTransform.getOrigin(); btVector3 offset = pos - origin; @@ -205,16 +215,16 @@ transform_changed() { * */ void BulletSoftBodyNode:: -sync_p2b() { +do_sync_p2b() { // transform_changed(); Disabled for now... } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletSoftBodyNode:: -sync_b2p() { +do_sync_b2p() { // Render softbody if (_geom) { @@ -266,7 +276,7 @@ sync_b2p() { // Update the synchronized transform with the current approximate center of // the soft body - LVecBase3 pos = this->get_aabb().get_approx_center(); + LVecBase3 pos = this->do_get_aabb().get_approx_center(); CPT(TransformState) ts = TransformState::make_pos(pos); NodePath np = NodePath::any_path((PandaNode *)this); @@ -291,6 +301,19 @@ sync_b2p() { */ int BulletSoftBodyNode:: get_closest_node_index(LVecBase3 point, bool local) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return do_get_closest_node_index(point, local); +} + +/** + * Returns the index of the node which is closest to the given point. The + * distance between each node and the given point is computed in world space + * if local=false, and in local space if local=true. + * Assumes the lock(bullet global lock) is held by the caller + */ +int BulletSoftBodyNode:: +do_get_closest_node_index(LVecBase3 point, bool local) { btScalar max_dist_sqr = 1e30; btVector3 point_x = LVecBase3_to_btVector3(point); @@ -322,11 +345,12 @@ get_closest_node_index(LVecBase3 point, bool local) { */ void BulletSoftBodyNode:: link_geom(Geom *geom) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(geom->get_vertex_data()->has_column(InternalName::get_vertex())); nassertv(geom->get_vertex_data()->has_column(InternalName::get_normal())); - sync_p2b(); + do_sync_p2b(); _geom = geom; @@ -349,7 +373,7 @@ link_geom(Geom *geom) { while (!vertices.is_at_end()) { LVecBase3 point = vertices.get_data3(); - int node_idx = get_closest_node_index(point, true); + int node_idx = do_get_closest_node_index(point, true); indices.set_data1i(node_idx); } } @@ -359,6 +383,7 @@ link_geom(Geom *geom) { */ void BulletSoftBodyNode:: unlink_geom() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _geom = NULL; } @@ -368,6 +393,7 @@ unlink_geom() { */ void BulletSoftBodyNode:: link_curve(NurbsCurveEvaluator *curve) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(curve->get_num_vertices() == _soft->m_nodes.size()); @@ -379,6 +405,7 @@ link_curve(NurbsCurveEvaluator *curve) { */ void BulletSoftBodyNode:: unlink_curve() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _curve = NULL; } @@ -388,6 +415,7 @@ unlink_curve() { */ void BulletSoftBodyNode:: link_surface(NurbsSurfaceEvaluator *surface) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(surface->get_num_u_vertices() * surface->get_num_v_vertices() == _soft->m_nodes.size()); @@ -399,6 +427,7 @@ link_surface(NurbsSurfaceEvaluator *surface) { */ void BulletSoftBodyNode:: unlink_surface() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _surface = NULL; } @@ -408,6 +437,16 @@ unlink_surface() { */ BoundingBox BulletSoftBodyNode:: get_aabb() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return do_get_aabb(); +} + +/** + * + */ +BoundingBox BulletSoftBodyNode:: +do_get_aabb() const { btVector3 pMin; btVector3 pMax; @@ -425,6 +464,7 @@ get_aabb() const { */ void BulletSoftBodyNode:: set_volume_mass(PN_stdfloat mass) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->setVolumeMass(mass); } @@ -434,6 +474,7 @@ set_volume_mass(PN_stdfloat mass) { */ void BulletSoftBodyNode:: set_total_mass(PN_stdfloat mass, bool fromfaces) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->setTotalMass(mass, fromfaces); } @@ -443,6 +484,7 @@ set_total_mass(PN_stdfloat mass, bool fromfaces) { */ void BulletSoftBodyNode:: set_volume_density(PN_stdfloat density) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->setVolumeDensity(density); } @@ -452,6 +494,7 @@ set_volume_density(PN_stdfloat density) { */ void BulletSoftBodyNode:: set_total_density(PN_stdfloat density) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->setTotalDensity(density); } @@ -461,6 +504,7 @@ set_total_density(PN_stdfloat density) { */ void BulletSoftBodyNode:: set_mass(int node, PN_stdfloat mass) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->setMass(node, mass); } @@ -470,6 +514,7 @@ set_mass(int node, PN_stdfloat mass) { */ PN_stdfloat BulletSoftBodyNode:: get_mass(int node) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _soft->getMass(node); } @@ -479,6 +524,7 @@ get_mass(int node) const { */ PN_stdfloat BulletSoftBodyNode:: get_total_mass() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _soft->getTotalMass(); } @@ -488,6 +534,7 @@ get_total_mass() const { */ PN_stdfloat BulletSoftBodyNode:: get_volume() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _soft->getVolume(); } @@ -497,6 +544,7 @@ get_volume() const { */ void BulletSoftBodyNode:: add_force(const LVector3 &force) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!force.is_nan()); _soft->addForce(LVecBase3_to_btVector3(force)); @@ -507,6 +555,7 @@ add_force(const LVector3 &force) { */ void BulletSoftBodyNode:: add_force(const LVector3 &force, int node) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!force.is_nan()); _soft->addForce(LVecBase3_to_btVector3(force), node); @@ -517,6 +566,7 @@ add_force(const LVector3 &force, int node) { */ void BulletSoftBodyNode:: set_velocity(const LVector3 &velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!velocity.is_nan()); _soft->setVelocity(LVecBase3_to_btVector3(velocity)); @@ -527,6 +577,7 @@ set_velocity(const LVector3 &velocity) { */ void BulletSoftBodyNode:: add_velocity(const LVector3 &velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!velocity.is_nan()); _soft->addVelocity(LVecBase3_to_btVector3(velocity)); @@ -537,6 +588,7 @@ add_velocity(const LVector3 &velocity) { */ void BulletSoftBodyNode:: add_velocity(const LVector3 &velocity, int node) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!velocity.is_nan()); _soft->addVelocity(LVecBase3_to_btVector3(velocity), node); @@ -547,6 +599,7 @@ add_velocity(const LVector3 &velocity, int node) { */ void BulletSoftBodyNode:: generate_clusters(int k, int maxiterations) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->generateClusters(k, maxiterations); } @@ -556,6 +609,7 @@ generate_clusters(int k, int maxiterations) { */ void BulletSoftBodyNode:: release_clusters() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->releaseClusters(); } @@ -565,6 +619,7 @@ release_clusters() { */ void BulletSoftBodyNode:: release_cluster(int index) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->releaseCluster(index); } @@ -574,6 +629,7 @@ release_cluster(int index) { */ int BulletSoftBodyNode:: get_num_clusters() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _soft->clusterCount(); } @@ -583,6 +639,7 @@ get_num_clusters() const { */ LVecBase3 BulletSoftBodyNode:: cluster_com(int cluster) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVecBase3(_soft->clusterCom(cluster)); } @@ -592,6 +649,7 @@ cluster_com(int cluster) const { */ void BulletSoftBodyNode:: set_pose(bool bvolume, bool bframe) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _soft->setPose(bvolume, bframe); } @@ -601,11 +659,12 @@ set_pose(bool bvolume, bool bframe) { */ void BulletSoftBodyNode:: append_anchor(int node, BulletRigidBodyNode *body, bool disable) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(node < _soft->m_nodes.size()) nassertv(body); - body->sync_p2b(); + body->do_sync_p2b(); btRigidBody *ptr = (btRigidBody *)body->get_object(); _soft->appendAnchor(node, ptr, disable); @@ -616,12 +675,13 @@ append_anchor(int node, BulletRigidBodyNode *body, bool disable) { */ void BulletSoftBodyNode:: append_anchor(int node, BulletRigidBodyNode *body, const LVector3 &pivot, bool disable) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(node < _soft->m_nodes.size()) nassertv(body); nassertv(!pivot.is_nan()); - body->sync_p2b(); + body->do_sync_p2b(); btRigidBody *ptr = (btRigidBody *)body->get_object(); _soft->appendAnchor(node, ptr, LVecBase3_to_btVector3(pivot), disable); @@ -642,6 +702,7 @@ BulletSoftBodyNodeElement(btSoftBody::Node &node) : _node(node) { */ int BulletSoftBodyNode:: get_point_index(LVecBase3 p, PTA_LVecBase3 points) { + LightMutexHolder holder(BulletWorld::get_global_lock()); PN_stdfloat eps = 1.0e-6f; // TODO make this a config option @@ -979,6 +1040,7 @@ make_tet_mesh(BulletSoftBodyWorldInfo &info, const char *ele, const char *face, */ void BulletSoftBodyNode:: append_linear_joint(BulletBodyNode *body, int cluster, PN_stdfloat erp, PN_stdfloat cfm, PN_stdfloat split) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(body); @@ -998,6 +1060,7 @@ append_linear_joint(BulletBodyNode *body, int cluster, PN_stdfloat erp, PN_stdfl */ void BulletSoftBodyNode:: append_linear_joint(BulletBodyNode *body, const LPoint3 &pos, PN_stdfloat erp, PN_stdfloat cfm, PN_stdfloat split) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(body); @@ -1017,6 +1080,7 @@ append_linear_joint(BulletBodyNode *body, const LPoint3 &pos, PN_stdfloat erp, P */ void BulletSoftBodyNode:: append_angular_joint(BulletBodyNode *body, const LVector3 &axis, PN_stdfloat erp, PN_stdfloat cfm, PN_stdfloat split, BulletSoftBodyControl *control) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(body); @@ -1037,6 +1101,7 @@ append_angular_joint(BulletBodyNode *body, const LVector3 &axis, PN_stdfloat erp */ void BulletSoftBodyNode:: set_wind_velocity(const LVector3 &velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!velocity.is_nan()); _soft->setWindVelocity(LVecBase3_to_btVector3(velocity)); @@ -1047,6 +1112,67 @@ set_wind_velocity(const LVector3 &velocity) { */ LVector3 BulletSoftBodyNode:: get_wind_velocity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_soft->getWindVelocity()); } + +/** + * + */ +LPoint3 BulletSoftBodyNodeElement:: +get_pos() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LPoint3(_node.m_x); +} + +/** + * + */ +LVector3 BulletSoftBodyNodeElement:: +get_normal() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_node.m_n); +} + +/** + * + */ +LVector3 BulletSoftBodyNodeElement:: +get_velocity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_node.m_v); +} + +/** + * + */ +PN_stdfloat BulletSoftBodyNodeElement:: +get_inv_mass() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_node.m_im; +} + +/** + * + */ +PN_stdfloat BulletSoftBodyNodeElement:: +get_area() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_node.m_area; +} + +/** + * + */ +int BulletSoftBodyNodeElement:: +is_attached() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_node.m_battach; +} diff --git a/panda/src/bullet/bulletSoftBodyNode.h b/panda/src/bullet/bulletSoftBodyNode.h index 0f37217dfe..600e185fac 100644 --- a/panda/src/bullet/bulletSoftBodyNode.h +++ b/panda/src/bullet/bulletSoftBodyNode.h @@ -43,12 +43,12 @@ PUBLISHED: INLINE ~BulletSoftBodyNodeElement(); INLINE static BulletSoftBodyNodeElement empty(); - INLINE LPoint3 get_pos() const; - INLINE LVector3 get_velocity() const; - INLINE LVector3 get_normal() const; - INLINE PN_stdfloat get_inv_mass() const; - INLINE PN_stdfloat get_area() const; - INLINE int is_attached() const; + LPoint3 get_pos() const; + LVector3 get_velocity() const; + LVector3 get_normal() const; + PN_stdfloat get_inv_mass() const; + PN_stdfloat get_area() const; + int is_attached() const; MAKE_PROPERTY(pos, get_pos); MAKE_PROPERTY(velocity, get_velocity); @@ -221,8 +221,8 @@ PUBLISHED: public: virtual btCollisionObject *get_object() const; - void sync_p2b(); - void sync_b2p(); + void do_sync_p2b(); + void do_sync_b2p(); protected: virtual void transform_changed(); @@ -240,6 +240,9 @@ private: static int get_point_index(LVecBase3 p, PTA_LVecBase3 points); static int next_line(const char *buffer); + BoundingBox do_get_aabb() const; + int do_get_closest_node_index(LVecBase3 point, bool local); + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/bullet/bulletSoftBodyShape.cxx b/panda/src/bullet/bulletSoftBodyShape.cxx index f28050ae3b..9e6aa2929b 100644 --- a/panda/src/bullet/bulletSoftBodyShape.cxx +++ b/panda/src/bullet/bulletSoftBodyShape.cxx @@ -40,6 +40,7 @@ ptr() const { */ BulletSoftBodyNode *BulletSoftBodyShape:: get_body() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); if (_shape->m_body) { return (BulletSoftBodyNode *)_shape->m_body->getUserPointer(); diff --git a/panda/src/bullet/bulletSoftBodyWorldInfo.cxx b/panda/src/bullet/bulletSoftBodyWorldInfo.cxx index ee1fddbc96..2fb6957c29 100644 --- a/panda/src/bullet/bulletSoftBodyWorldInfo.cxx +++ b/panda/src/bullet/bulletSoftBodyWorldInfo.cxx @@ -26,6 +26,7 @@ BulletSoftBodyWorldInfo(btSoftBodyWorldInfo &info) : _info(info) { */ void BulletSoftBodyWorldInfo:: garbage_collect(int lifetime) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_sparsesdf.GarbageCollect(lifetime); } @@ -35,6 +36,7 @@ garbage_collect(int lifetime) { */ void BulletSoftBodyWorldInfo:: set_air_density(PN_stdfloat density) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.air_density = (btScalar)density; } @@ -44,6 +46,7 @@ set_air_density(PN_stdfloat density) { */ void BulletSoftBodyWorldInfo:: set_water_density(PN_stdfloat density) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.water_density = (btScalar)density; } @@ -53,6 +56,7 @@ set_water_density(PN_stdfloat density) { */ void BulletSoftBodyWorldInfo:: set_water_offset(PN_stdfloat offset) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.water_offset = (btScalar)offset; } @@ -62,6 +66,7 @@ set_water_offset(PN_stdfloat offset) { */ void BulletSoftBodyWorldInfo:: set_water_normal(const LVector3 &normal) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!normal.is_nan()); _info.water_normal.setValue(normal.get_x(), normal.get_y(), normal.get_z()); @@ -72,6 +77,7 @@ set_water_normal(const LVector3 &normal) { */ void BulletSoftBodyWorldInfo:: set_gravity(const LVector3 &gravity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!gravity.is_nan()); _info.m_gravity.setValue(gravity.get_x(), gravity.get_y(), gravity.get_z()); @@ -82,6 +88,7 @@ set_gravity(const LVector3 &gravity) { */ PN_stdfloat BulletSoftBodyWorldInfo:: get_air_density() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.air_density; } @@ -91,6 +98,7 @@ get_air_density() const { */ PN_stdfloat BulletSoftBodyWorldInfo:: get_water_density() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.water_density; } @@ -100,6 +108,7 @@ get_water_density() const { */ PN_stdfloat BulletSoftBodyWorldInfo:: get_water_offset() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.water_offset; } @@ -109,6 +118,7 @@ get_water_offset() const { */ LVector3 BulletSoftBodyWorldInfo:: get_water_normal() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_info.water_normal); } @@ -118,6 +128,7 @@ get_water_normal() const { */ LVector3 BulletSoftBodyWorldInfo:: get_gravity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_info.m_gravity); } diff --git a/panda/src/bullet/bulletSphereShape.I b/panda/src/bullet/bulletSphereShape.I index a5177c572a..d410322298 100644 --- a/panda/src/bullet/bulletSphereShape.I +++ b/panda/src/bullet/bulletSphereShape.I @@ -20,24 +20,6 @@ INLINE BulletSphereShape:: delete _shape; } -/** - * - */ -INLINE BulletSphereShape:: -BulletSphereShape(const BulletSphereShape ©) : - _shape(copy._shape), - _radius(copy._radius) { -} - -/** - * - */ -INLINE void BulletSphereShape:: -operator = (const BulletSphereShape ©) { - _shape = copy._shape; - _radius = copy._radius; -} - /** * Returns the radius that was used to construct this sphere. */ diff --git a/panda/src/bullet/bulletSphereShape.cxx b/panda/src/bullet/bulletSphereShape.cxx index 4cda14882b..7b3fee62a4 100644 --- a/panda/src/bullet/bulletSphereShape.cxx +++ b/panda/src/bullet/bulletSphereShape.cxx @@ -25,6 +25,29 @@ BulletSphereShape(PN_stdfloat radius) : _radius(radius) { _shape->setUserPointer(this); } +/** + * + */ +BulletSphereShape:: +BulletSphereShape(const BulletSphereShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _radius = copy._radius; +} + +/** + * + */ +void BulletSphereShape:: +operator = (const BulletSphereShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _shape = copy._shape; + _radius = copy._radius; +} + + /** * */ diff --git a/panda/src/bullet/bulletSphereShape.h b/panda/src/bullet/bulletSphereShape.h index c16a1279a6..b11d1c4a32 100644 --- a/panda/src/bullet/bulletSphereShape.h +++ b/panda/src/bullet/bulletSphereShape.h @@ -32,8 +32,8 @@ private: PUBLISHED: explicit BulletSphereShape(PN_stdfloat radius); - INLINE BulletSphereShape(const BulletSphereShape ©); - INLINE void operator = (const BulletSphereShape ©); + BulletSphereShape(const BulletSphereShape ©); + void operator = (const BulletSphereShape ©); INLINE ~BulletSphereShape(); INLINE PN_stdfloat get_radius() const; diff --git a/panda/src/bullet/bulletSphericalConstraint.cxx b/panda/src/bullet/bulletSphericalConstraint.cxx index 021dd69777..8686e1d546 100644 --- a/panda/src/bullet/bulletSphericalConstraint.cxx +++ b/panda/src/bullet/bulletSphericalConstraint.cxx @@ -61,6 +61,7 @@ ptr() const { */ void BulletSphericalConstraint:: set_pivot_a(const LPoint3 &pivot_a) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!pivot_a.is_nan()); _constraint->setPivotA(LVecBase3_to_btVector3(pivot_a)); @@ -71,6 +72,7 @@ set_pivot_a(const LPoint3 &pivot_a) { */ void BulletSphericalConstraint:: set_pivot_b(const LPoint3 &pivot_b) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!pivot_b.is_nan()); _constraint->setPivotB(LVecBase3_to_btVector3(pivot_b)); @@ -81,6 +83,7 @@ set_pivot_b(const LPoint3 &pivot_b) { */ LPoint3 BulletSphericalConstraint:: get_pivot_in_a() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LPoint3(_constraint->getPivotInA()); } @@ -90,6 +93,7 @@ get_pivot_in_a() const { */ LPoint3 BulletSphericalConstraint:: get_pivot_in_b() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LPoint3(_constraint->getPivotInB()); } diff --git a/panda/src/bullet/bulletTranslationalLimitMotor.I b/panda/src/bullet/bulletTranslationalLimitMotor.I index 2972994e39..7b41ab9618 100644 --- a/panda/src/bullet/bulletTranslationalLimitMotor.I +++ b/panda/src/bullet/bulletTranslationalLimitMotor.I @@ -14,164 +14,7 @@ /** * */ -INLINE bool BulletTranslationalLimitMotor:: -is_limited(int axis) const { +INLINE BulletTranslationalLimitMotor:: +~BulletTranslationalLimitMotor() { - nassertr((0 <= axis) && (axis <= 2), false); - return _motor.isLimited(axis); -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_motor_enabled(int axis, bool enabled) { - - nassertv((0 <= axis) && (axis <= 2)); - _motor.m_enableMotor[axis] = enabled; -} - -/** - * - */ -INLINE bool BulletTranslationalLimitMotor:: -get_motor_enabled(int axis) const { - - nassertr((0 <= axis) && (axis <= 2), false); - return _motor.m_enableMotor[axis]; -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_low_limit(const LVecBase3 &limit) { - - nassertv(!limit.is_nan()); - _motor.m_lowerLimit = LVecBase3_to_btVector3(limit); -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_high_limit(const LVecBase3 &limit) { - - nassertv(!limit.is_nan()); - _motor.m_upperLimit = LVecBase3_to_btVector3(limit); -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_target_velocity(const LVecBase3 &velocity) { - - nassertv(!velocity.is_nan()); - _motor.m_targetVelocity = LVecBase3_to_btVector3(velocity); -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_max_motor_force(const LVecBase3 &force) { - - nassertv(!force.is_nan()); - _motor.m_maxMotorForce = LVecBase3_to_btVector3(force); -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_damping(PN_stdfloat damping) { - - _motor.m_damping = (btScalar)damping; -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_softness(PN_stdfloat softness) { - - _motor.m_limitSoftness = (btScalar)softness; -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_restitution(PN_stdfloat restitution) { - - _motor.m_restitution = (btScalar)restitution; -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_normal_cfm(const LVecBase3 &cfm) { - - nassertv(!cfm.is_nan()); - _motor.m_normalCFM = LVecBase3_to_btVector3(cfm); -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_stop_cfm(const LVecBase3 &cfm) { - - nassertv(!cfm.is_nan()); - _motor.m_stopCFM = LVecBase3_to_btVector3(cfm); -} - -/** - * - */ -INLINE void BulletTranslationalLimitMotor:: -set_stop_erp(const LVecBase3 &erp) { - - nassertv(!erp.is_nan()); - _motor.m_stopERP = LVecBase3_to_btVector3(erp); -} - -/** - * Retrieves the current value of angle: 0 = free, 1 = at low limit, 2 = at - * high limit. - */ -INLINE int BulletTranslationalLimitMotor:: -get_current_limit(int axis) const { - - nassertr((0 <= axis) && (axis <= 2), false); - return _motor.m_currentLimit[axis]; -} - -/** - * - */ -INLINE LVector3 BulletTranslationalLimitMotor:: -get_current_error() const { - - return btVector3_to_LVector3(_motor.m_currentLimitError); -} - -/** - * - */ -INLINE LPoint3 BulletTranslationalLimitMotor:: -get_current_diff() const { - - return btVector3_to_LPoint3(_motor.m_currentLinearDiff); -} - -/** - * - */ -INLINE LVector3 BulletTranslationalLimitMotor:: -get_accumulated_impulse() const { - - return btVector3_to_LVector3(_motor.m_accumulatedImpulse); } diff --git a/panda/src/bullet/bulletTranslationalLimitMotor.cxx b/panda/src/bullet/bulletTranslationalLimitMotor.cxx index fdf0b55501..48dd7ae3e8 100644 --- a/panda/src/bullet/bulletTranslationalLimitMotor.cxx +++ b/panda/src/bullet/bulletTranslationalLimitMotor.cxx @@ -34,7 +34,182 @@ BulletTranslationalLimitMotor(const BulletTranslationalLimitMotor ©) /** * */ -BulletTranslationalLimitMotor:: -~BulletTranslationalLimitMotor() { +bool BulletTranslationalLimitMotor:: +is_limited(int axis) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + nassertr((0 <= axis) && (axis <= 2), false); + return _motor.isLimited(axis); +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_motor_enabled(int axis, bool enabled) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv((0 <= axis) && (axis <= 2)); + _motor.m_enableMotor[axis] = enabled; +} + +/** + * + */ +bool BulletTranslationalLimitMotor:: +get_motor_enabled(int axis) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertr((0 <= axis) && (axis <= 2), false); + return _motor.m_enableMotor[axis]; +} + + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_low_limit(const LVecBase3 &limit) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(!limit.is_nan()); + _motor.m_lowerLimit = LVecBase3_to_btVector3(limit); +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_high_limit(const LVecBase3 &limit) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(!limit.is_nan()); + _motor.m_upperLimit = LVecBase3_to_btVector3(limit); +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_target_velocity(const LVecBase3 &velocity) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(!velocity.is_nan()); + _motor.m_targetVelocity = LVecBase3_to_btVector3(velocity); +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_max_motor_force(const LVecBase3 &force) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(!force.is_nan()); + _motor.m_maxMotorForce = LVecBase3_to_btVector3(force); +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_damping(PN_stdfloat damping) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_damping = (btScalar)damping; +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_softness(PN_stdfloat softness) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_limitSoftness = (btScalar)softness; +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_restitution(PN_stdfloat restitution) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _motor.m_restitution = (btScalar)restitution; +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_normal_cfm(const LVecBase3 &cfm) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(!cfm.is_nan()); + _motor.m_normalCFM = LVecBase3_to_btVector3(cfm); +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_stop_cfm(const LVecBase3 &cfm) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(!cfm.is_nan()); + _motor.m_stopCFM = LVecBase3_to_btVector3(cfm); +} + +/** + * + */ +void BulletTranslationalLimitMotor:: +set_stop_erp(const LVecBase3 &erp) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertv(!erp.is_nan()); + _motor.m_stopERP = LVecBase3_to_btVector3(erp); +} + +/** + * Retrieves the current value of angle: 0 = free, 1 = at low limit, 2 = at + * high limit. + */ +int BulletTranslationalLimitMotor:: +get_current_limit(int axis) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertr((0 <= axis) && (axis <= 2), false); + return _motor.m_currentLimit[axis]; +} + +/** + * + */ +LVector3 BulletTranslationalLimitMotor:: +get_current_error() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_motor.m_currentLimitError); +} + +/** + * + */ +LPoint3 BulletTranslationalLimitMotor:: +get_current_diff() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LPoint3(_motor.m_currentLinearDiff); +} + +/** + * + */ +LVector3 BulletTranslationalLimitMotor:: +get_accumulated_impulse() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_motor.m_accumulatedImpulse); } diff --git a/panda/src/bullet/bulletTranslationalLimitMotor.h b/panda/src/bullet/bulletTranslationalLimitMotor.h index f339daefef..97c9909ede 100644 --- a/panda/src/bullet/bulletTranslationalLimitMotor.h +++ b/panda/src/bullet/bulletTranslationalLimitMotor.h @@ -28,26 +28,26 @@ class EXPCL_PANDABULLET BulletTranslationalLimitMotor { PUBLISHED: BulletTranslationalLimitMotor(const BulletTranslationalLimitMotor ©); - ~BulletTranslationalLimitMotor(); + INLINE ~BulletTranslationalLimitMotor(); - INLINE void set_motor_enabled(int axis, bool enable); - INLINE void set_low_limit(const LVecBase3 &limit); - INLINE void set_high_limit(const LVecBase3 &limit); - INLINE void set_target_velocity(const LVecBase3 &velocity); - INLINE void set_max_motor_force(const LVecBase3 &force); - INLINE void set_damping(PN_stdfloat damping); - INLINE void set_softness(PN_stdfloat softness); - INLINE void set_restitution(PN_stdfloat restitution); - INLINE void set_normal_cfm(const LVecBase3 &cfm); - INLINE void set_stop_erp(const LVecBase3 &erp); - INLINE void set_stop_cfm(const LVecBase3 &cfm); + void set_motor_enabled(int axis, bool enable); + void set_low_limit(const LVecBase3 &limit); + void set_high_limit(const LVecBase3 &limit); + void set_target_velocity(const LVecBase3 &velocity); + void set_max_motor_force(const LVecBase3 &force); + void set_damping(PN_stdfloat damping); + void set_softness(PN_stdfloat softness); + void set_restitution(PN_stdfloat restitution); + void set_normal_cfm(const LVecBase3 &cfm); + void set_stop_erp(const LVecBase3 &erp); + void set_stop_cfm(const LVecBase3 &cfm); - INLINE bool is_limited(int axis) const; - INLINE bool get_motor_enabled(int axis) const; - INLINE int get_current_limit(int axis) const; - INLINE LVector3 get_current_error() const; - INLINE LPoint3 get_current_diff() const; - INLINE LVector3 get_accumulated_impulse() const; + bool is_limited(int axis) const; + bool get_motor_enabled(int axis) const; + int get_current_limit(int axis) const; + LVector3 get_current_error() const; + LPoint3 get_current_diff() const; + LVector3 get_accumulated_impulse() const; MAKE_PROPERTY(current_error, get_current_error); MAKE_PROPERTY(current_diff, get_current_diff); diff --git a/panda/src/bullet/bulletTriangleMesh.I b/panda/src/bullet/bulletTriangleMesh.I index b6965303aa..33b9b9bf47 100644 --- a/panda/src/bullet/bulletTriangleMesh.I +++ b/panda/src/bullet/bulletTriangleMesh.I @@ -19,34 +19,6 @@ ptr() const { return (btStridingMeshInterface *)&_mesh; } -/** - * Returns the number of vertices in this triangle mesh. - */ -INLINE size_t BulletTriangleMesh:: -get_num_vertices() const { - return _vertices.size(); -} - -/** - * Returns the vertex at the given vertex index. - */ -INLINE LPoint3 BulletTriangleMesh:: -get_vertex(size_t index) const { - nassertr(index < _vertices.size(), LPoint3::zero()); - const btVector3 &vertex = _vertices[index]; - return LPoint3(vertex[0], vertex[1], vertex[2]); -} - -/** - * Returns the vertex indices making up the given triangle index. - */ -INLINE LVecBase3i BulletTriangleMesh:: -get_triangle(size_t index) const { - index *= 3; - nassertr(index + 2 < _indices.size(), LVecBase3i::zero()); - return LVecBase3i(_indices[index], _indices[index + 1], _indices[index + 2]); -} - /** * */ diff --git a/panda/src/bullet/bulletTriangleMesh.cxx b/panda/src/bullet/bulletTriangleMesh.cxx index 3138e28efd..e2d3e0b242 100644 --- a/panda/src/bullet/bulletTriangleMesh.cxx +++ b/panda/src/bullet/bulletTriangleMesh.cxx @@ -36,12 +36,58 @@ BulletTriangleMesh() _mesh.addIndexedMesh(mesh); } +/** + * Returns the number of vertices in this triangle mesh. + */ +size_t BulletTriangleMesh:: +get_num_vertices() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _vertices.size(); +} + +/** + * Returns the vertex at the given vertex index. + */ +LPoint3 BulletTriangleMesh:: +get_vertex(size_t index) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + nassertr(index < _vertices.size(), LPoint3::zero()); + const btVector3 &vertex = _vertices[index]; + return LPoint3(vertex[0], vertex[1], vertex[2]); +} + +/** + * Returns the vertex indices making up the given triangle index. + */ +LVecBase3i BulletTriangleMesh:: +get_triangle(size_t index) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + index *= 3; + nassertr(index + 2 < _indices.size(), LVecBase3i::zero()); + return LVecBase3i(_indices[index], _indices[index + 1], _indices[index + 2]); +} + +/** + * Returns the number of triangles in this triangle mesh. + * Assumes the lock(bullet global lock) is held by the caller + */ +size_t BulletTriangleMesh:: +do_get_num_triangles() const { + + return _indices.size() / 3; +} + /** * Returns the number of triangles in this triangle mesh. */ size_t BulletTriangleMesh:: get_num_triangles() const { - return _indices.size() / 3; + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return do_get_num_triangles(); } /** @@ -51,6 +97,8 @@ get_num_triangles() const { */ void BulletTriangleMesh:: preallocate(int num_verts, int num_indices) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + _vertices.reserve(num_verts); _indices.reserve(num_indices); @@ -66,9 +114,11 @@ preallocate(int num_verts, int num_indices) { * add duplicate vertices if they already exist in the triangle mesh, within * the tolerance specified by set_welding_distance(). This comes at a * significant performance cost, especially for large meshes. + * Assumes the lock(bullet global lock) is held by the caller */ void BulletTriangleMesh:: -add_triangle(const LPoint3 &p0, const LPoint3 &p1, const LPoint3 &p2, bool remove_duplicate_vertices) { +do_add_triangle(const LPoint3 &p0, const LPoint3 &p1, const LPoint3 &p2, bool remove_duplicate_vertices) { + nassertv(!p0.is_nan()); nassertv(!p1.is_nan()); nassertv(!p2.is_nan()); @@ -96,6 +146,21 @@ add_triangle(const LPoint3 &p0, const LPoint3 &p1, const LPoint3 &p2, bool remov mesh.m_triangleIndexBase = (unsigned char *)&_indices[0]; } +/** + * Adds a triangle with the indicated coordinates. + * + * If remove_duplicate_vertices is true, it will make sure that it does not + * add duplicate vertices if they already exist in the triangle mesh, within + * the tolerance specified by set_welding_distance(). This comes at a + * significant performance cost, especially for large meshes. + */ +void BulletTriangleMesh:: +add_triangle(const LPoint3 &p0, const LPoint3 &p1, const LPoint3 &p2, bool remove_duplicate_vertices) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + do_add_triangle(p0, p1, p2, remove_duplicate_vertices); +} + /** * Sets the square of the distance at which vertices will be merged * together when adding geometry with remove_duplicate_vertices set to true. @@ -105,6 +170,8 @@ add_triangle(const LPoint3 &p0, const LPoint3 &p1, const LPoint3 &p2, bool remov */ void BulletTriangleMesh:: set_welding_distance(PN_stdfloat distance) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + _welding_distance = distance; } @@ -114,6 +181,8 @@ set_welding_distance(PN_stdfloat distance) { */ PN_stdfloat BulletTriangleMesh:: get_welding_distance() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + return _welding_distance; } @@ -129,6 +198,8 @@ get_welding_distance() const { */ void BulletTriangleMesh:: add_geom(const Geom *geom, bool remove_duplicate_vertices, const TransformState *ts) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + nassertv(geom); nassertv(ts); @@ -241,6 +312,8 @@ add_geom(const Geom *geom, bool remove_duplicate_vertices, const TransformState */ void BulletTriangleMesh:: add_array(const PTA_LVecBase3 &points, const PTA_int &indices, bool remove_duplicate_vertices) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + btIndexedMesh &mesh = _mesh.getIndexedMeshArray()[0]; _indices.reserve(_indices.size() + indices.size()); @@ -279,7 +352,9 @@ add_array(const PTA_LVecBase3 &points, const PTA_int &indices, bool remove_dupli */ void BulletTriangleMesh:: output(ostream &out) const { - out << get_type() << ", " << get_num_triangles() << " triangles"; + LightMutexHolder holder(BulletWorld::get_global_lock()); + + out << get_type() << ", " << _indices.size() / 3 << " triangles"; } /** diff --git a/panda/src/bullet/bulletTriangleMesh.h b/panda/src/bullet/bulletTriangleMesh.h index 64973d83e8..0f9cb8174b 100644 --- a/panda/src/bullet/bulletTriangleMesh.h +++ b/panda/src/bullet/bulletTriangleMesh.h @@ -55,10 +55,16 @@ PUBLISHED: virtual void write(ostream &out, int indent_level) const; public: - INLINE size_t get_num_vertices() const; - INLINE LPoint3 get_vertex(size_t index) const; + size_t get_num_vertices() const; + LPoint3 get_vertex(size_t index) const; - INLINE LVecBase3i get_triangle(size_t index) const; + LVecBase3i get_triangle(size_t index) const; + + size_t do_get_num_triangles() const; + void do_add_triangle(const LPoint3 &p0, + const LPoint3 &p1, + const LPoint3 &p2, + bool remove_duplicate_vertices=false); PUBLISHED: MAKE_PROPERTY(welding_distance, get_welding_distance, set_welding_distance); diff --git a/panda/src/bullet/bulletTriangleMeshShape.I b/panda/src/bullet/bulletTriangleMeshShape.I index abb51607b4..f178723ebc 100644 --- a/panda/src/bullet/bulletTriangleMeshShape.I +++ b/panda/src/bullet/bulletTriangleMeshShape.I @@ -11,27 +11,6 @@ * @date 2010-02-09 */ -/** - * - */ -INLINE BulletTriangleMeshShape:: -BulletTriangleMeshShape(const BulletTriangleMeshShape ©) : - _bvh_shape(copy._bvh_shape), - _gimpact_shape(copy._gimpact_shape), - _mesh(copy._mesh) { -} - -/** - * - */ -INLINE void BulletTriangleMeshShape:: -operator = (const BulletTriangleMeshShape ©) { - - _bvh_shape = copy._bvh_shape; - _gimpact_shape = copy._gimpact_shape; - _mesh = copy._mesh; -} - /** * */ diff --git a/panda/src/bullet/bulletTriangleMeshShape.cxx b/panda/src/bullet/bulletTriangleMeshShape.cxx index 22775a6e0a..61905a406a 100644 --- a/panda/src/bullet/bulletTriangleMeshShape.cxx +++ b/panda/src/bullet/bulletTriangleMeshShape.cxx @@ -36,6 +36,7 @@ BulletTriangleMeshShape() : /** * The parameters 'compress' and 'bvh' are only used if 'dynamic' is set to * FALSE. + * Assumes the lock(bullet global lock) is held by the caller */ BulletTriangleMeshShape:: BulletTriangleMeshShape(BulletTriangleMesh *mesh, bool dynamic, bool compress, bool bvh) : @@ -50,7 +51,7 @@ BulletTriangleMeshShape(BulletTriangleMesh *mesh, bool dynamic, bool compress, b } // Assert that mesh has at least one triangle - if (mesh->get_num_triangles() == 0) { + if (mesh->do_get_num_triangles() == 0) { bullet_cat.warning() << "mesh has zero triangles! adding degenerated triangle." << endl; mesh->add_triangle(LPoint3::zero(), LPoint3::zero(), LPoint3::zero()); } @@ -78,6 +79,30 @@ BulletTriangleMeshShape(BulletTriangleMesh *mesh, bool dynamic, bool compress, b } } +/** + * + */ +BulletTriangleMeshShape:: +BulletTriangleMeshShape(const BulletTriangleMeshShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _bvh_shape = copy._bvh_shape; + _gimpact_shape = copy._gimpact_shape; + _mesh = copy._mesh; +} + +/** + * + */ +void BulletTriangleMeshShape:: +operator = (const BulletTriangleMeshShape ©) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _bvh_shape = copy._bvh_shape; + _gimpact_shape = copy._gimpact_shape; + _mesh = copy._mesh; +} + /** * */ @@ -100,6 +125,7 @@ ptr() const { */ void BulletTriangleMeshShape:: refit_tree(const LPoint3 &aabb_min, const LPoint3 &aabb_max) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!aabb_max.is_nan()); nassertv(!aabb_max.is_nan()); diff --git a/panda/src/bullet/bulletTriangleMeshShape.h b/panda/src/bullet/bulletTriangleMeshShape.h index b76227b46e..079fb2aeb1 100644 --- a/panda/src/bullet/bulletTriangleMeshShape.h +++ b/panda/src/bullet/bulletTriangleMeshShape.h @@ -32,8 +32,8 @@ private: PUBLISHED: explicit BulletTriangleMeshShape(BulletTriangleMesh *mesh, bool dynamic, bool compress=true, bool bvh=true); - INLINE BulletTriangleMeshShape(const BulletTriangleMeshShape ©); - INLINE void operator = (const BulletTriangleMeshShape ©); + BulletTriangleMeshShape(const BulletTriangleMeshShape ©); + void operator = (const BulletTriangleMeshShape ©); INLINE ~BulletTriangleMeshShape(); void refit_tree(const LPoint3 &aabb_min, const LPoint3 &aabb_max); diff --git a/panda/src/bullet/bulletVehicle.I b/panda/src/bullet/bulletVehicle.I index b167ab5303..f4599a63e4 100644 --- a/panda/src/bullet/bulletVehicle.I +++ b/panda/src/bullet/bulletVehicle.I @@ -18,6 +18,7 @@ INLINE BulletVehicle:: ~BulletVehicle() { delete _vehicle; + delete _raycaster; } /** @@ -40,119 +41,3 @@ get_tuning() { return _tuning; } -/** - * Returns the number of wheels this vehicle has. - */ -INLINE int BulletVehicle:: -get_num_wheels() const { - - return _vehicle->getNumWheels(); -} - -/** - * - */ -void BulletVehicleTuning:: -set_suspension_stiffness(PN_stdfloat value) { - - _.m_suspensionStiffness = (btScalar)value; -} - -/** - * - */ -void BulletVehicleTuning:: -set_suspension_compression(PN_stdfloat value) { - - _.m_suspensionCompression = (btScalar)value; -} - -/** - * - */ -void BulletVehicleTuning:: -set_suspension_damping(PN_stdfloat value) { - - _.m_suspensionDamping = (btScalar)value; -} - -/** - * - */ -void BulletVehicleTuning:: -set_max_suspension_travel_cm(PN_stdfloat value) { - - _.m_maxSuspensionTravelCm = (btScalar)value; -} - -/** - * - */ -void BulletVehicleTuning:: -set_friction_slip(PN_stdfloat value) { - - _.m_frictionSlip = (btScalar)value; -} - -/** - * - */ -void BulletVehicleTuning:: -set_max_suspension_force(PN_stdfloat value) { - - _.m_maxSuspensionForce = (btScalar)value; -} - -/** - * - */ -PN_stdfloat BulletVehicleTuning:: -get_suspension_stiffness() const { - - return (PN_stdfloat)_.m_suspensionStiffness; -} - -/** - * - */ -PN_stdfloat BulletVehicleTuning:: -get_suspension_compression() const { - - return (PN_stdfloat)_.m_suspensionCompression; -} - -/** - * - */ -PN_stdfloat BulletVehicleTuning:: -get_suspension_damping() const { - - return (PN_stdfloat)_.m_suspensionDamping; -} - -/** - * - */ -PN_stdfloat BulletVehicleTuning:: -get_max_suspension_travel_cm() const { - - return (PN_stdfloat)_.m_maxSuspensionTravelCm; -} - -/** - * - */ -PN_stdfloat BulletVehicleTuning:: -get_friction_slip() const { - - return (PN_stdfloat)_.m_frictionSlip; -} - -/** - * - */ -PN_stdfloat BulletVehicleTuning:: -get_max_suspension_force() const { - - return (PN_stdfloat)_.m_maxSuspensionForce; -} diff --git a/panda/src/bullet/bulletVehicle.cxx b/panda/src/bullet/bulletVehicle.cxx index 381f8ab6c8..e2d74c131d 100644 --- a/panda/src/bullet/bulletVehicle.cxx +++ b/panda/src/bullet/bulletVehicle.cxx @@ -39,6 +39,7 @@ BulletVehicle(BulletWorld *world, BulletRigidBodyNode *chassis) { */ void BulletVehicle:: set_coordinate_system(BulletUpAxis up) { + LightMutexHolder holder(BulletWorld::get_global_lock()); switch (up) { case X_up: @@ -62,18 +63,30 @@ set_coordinate_system(BulletUpAxis up) { */ LVector3 BulletVehicle:: get_forward_vector() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_vehicle->getForwardVector()); } +/** + * Returns the chassis of this vehicle. The chassis is a rigid body node. + * Assumes the lock(bullet global lock) is held by the caller + */ +BulletRigidBodyNode *BulletVehicle:: +do_get_chassis() { + + btRigidBody *bodyPtr = _vehicle->getRigidBody(); + return (bodyPtr) ? (BulletRigidBodyNode *)bodyPtr->getUserPointer() : NULL; +} + /** * Returns the chassis of this vehicle. The chassis is a rigid body node. */ BulletRigidBodyNode *BulletVehicle:: get_chassis() { + LightMutexHolder holder(BulletWorld::get_global_lock()); - btRigidBody *bodyPtr = _vehicle->getRigidBody(); - return (bodyPtr) ? (BulletRigidBodyNode *)bodyPtr->getUserPointer() : NULL; + return do_get_chassis(); } /** @@ -82,6 +95,7 @@ get_chassis() { */ PN_stdfloat BulletVehicle:: get_current_speed_km_hour() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_vehicle->getCurrentSpeedKmHour(); } @@ -91,6 +105,7 @@ get_current_speed_km_hour() const { */ void BulletVehicle:: reset_suspension() { + LightMutexHolder holder(BulletWorld::get_global_lock()); _vehicle->resetSuspension(); } @@ -100,8 +115,9 @@ reset_suspension() { */ PN_stdfloat BulletVehicle:: get_steering_value(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); - nassertr(idx < get_num_wheels(), 0.0f); + nassertr(idx < _vehicle->getNumWheels(), 0.0f); return rad_2_deg(_vehicle->getSteeringValue(idx)); } @@ -110,8 +126,9 @@ get_steering_value(int idx) const { */ void BulletVehicle:: set_steering_value(PN_stdfloat steering, int idx) { + LightMutexHolder holder(BulletWorld::get_global_lock()); - nassertv(idx < get_num_wheels()); + nassertv(idx < _vehicle->getNumWheels()); _vehicle->setSteeringValue(deg_2_rad(steering), idx); } @@ -120,8 +137,9 @@ set_steering_value(PN_stdfloat steering, int idx) { */ void BulletVehicle:: apply_engine_force(PN_stdfloat force, int idx) { + LightMutexHolder holder(BulletWorld::get_global_lock()); - nassertv(idx < get_num_wheels()); + nassertv(idx < _vehicle->getNumWheels()); _vehicle->applyEngineForce(force, idx); } @@ -130,8 +148,9 @@ apply_engine_force(PN_stdfloat force, int idx) { */ void BulletVehicle:: set_brake(PN_stdfloat brake, int idx) { + LightMutexHolder holder(BulletWorld::get_global_lock()); - nassertv(idx < get_num_wheels()); + nassertv(idx < _vehicle->getNumWheels()); _vehicle->setBrake(brake, idx); } @@ -140,6 +159,7 @@ set_brake(PN_stdfloat brake, int idx) { */ void BulletVehicle:: set_pitch_control(PN_stdfloat pitch) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _vehicle->setPitchControl(pitch); } @@ -149,6 +169,7 @@ set_pitch_control(PN_stdfloat pitch) { */ BulletWheel BulletVehicle:: create_wheel() { + LightMutexHolder holder(BulletWorld::get_global_lock()); btVector3 pos(0.0, 0.0, 0.0); btVector3 direction = get_axis(_vehicle->getUpAxis()); @@ -182,24 +203,35 @@ get_axis(int idx) { } } +/** + * Returns the number of wheels this vehicle has. + */ +int BulletVehicle:: +get_num_wheels() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _vehicle->getNumWheels(); +} + /** * Returns the BulletWheel with index idx. Causes an AssertionError if idx is * equal or larger than the number of wheels. */ BulletWheel BulletVehicle:: get_wheel(int idx) const { + LightMutexHolder holder(BulletWorld::get_global_lock()); - nassertr(idx < get_num_wheels(), BulletWheel::empty()); + nassertr(idx < _vehicle->getNumWheels(), BulletWheel::empty()); return BulletWheel(_vehicle->getWheelInfo(idx)); } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletVehicle:: -sync_b2p() { +do_sync_b2p() { - for (int i=0; i < get_num_wheels(); i++) { + for (int i=0; i < _vehicle->getNumWheels(); i++) { btWheelInfo info = _vehicle->getWheelInfo(i); PandaNode *node = (PandaNode *)info.m_clientInfo; @@ -216,3 +248,124 @@ sync_b2p() { } } } + +/** + * + */ +void BulletVehicleTuning:: +set_suspension_stiffness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _.m_suspensionStiffness = (btScalar)value; +} + +/** + * + */ +void BulletVehicleTuning:: +set_suspension_compression(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _.m_suspensionCompression = (btScalar)value; +} + +/** + * + */ +void BulletVehicleTuning:: +set_suspension_damping(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _.m_suspensionDamping = (btScalar)value; +} + +/** + * + */ +void BulletVehicleTuning:: +set_max_suspension_travel_cm(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _.m_maxSuspensionTravelCm = (btScalar)value; +} + +/** + * + */ +void BulletVehicleTuning:: +set_friction_slip(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _.m_frictionSlip = (btScalar)value; +} + +/** + * + */ +void BulletVehicleTuning:: +set_max_suspension_force(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + _.m_maxSuspensionForce = (btScalar)value; +} + +/** + * + */ +PN_stdfloat BulletVehicleTuning:: +get_suspension_stiffness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_.m_suspensionStiffness; +} + +/** + * + */ +PN_stdfloat BulletVehicleTuning:: +get_suspension_compression() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_.m_suspensionCompression; +} + +/** + * + */ +PN_stdfloat BulletVehicleTuning:: +get_suspension_damping() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_.m_suspensionDamping; +} + +/** + * + */ +PN_stdfloat BulletVehicleTuning:: +get_max_suspension_travel_cm() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_.m_maxSuspensionTravelCm; +} + +/** + * + */ +PN_stdfloat BulletVehicleTuning:: +get_friction_slip() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_.m_frictionSlip; +} + +/** + * + */ +PN_stdfloat BulletVehicleTuning:: +get_max_suspension_force() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return (PN_stdfloat)_.m_maxSuspensionForce; +} + diff --git a/panda/src/bullet/bulletVehicle.h b/panda/src/bullet/bulletVehicle.h index 8b7f7862c6..9f9da751c3 100644 --- a/panda/src/bullet/bulletVehicle.h +++ b/panda/src/bullet/bulletVehicle.h @@ -32,19 +32,19 @@ class BulletWheel; class EXPCL_PANDABULLET BulletVehicleTuning { PUBLISHED: - INLINE void set_suspension_stiffness(PN_stdfloat value); - INLINE void set_suspension_compression(PN_stdfloat value); - INLINE void set_suspension_damping(PN_stdfloat value); - INLINE void set_max_suspension_travel_cm(PN_stdfloat value); - INLINE void set_friction_slip(PN_stdfloat value); - INLINE void set_max_suspension_force(PN_stdfloat value); + void set_suspension_stiffness(PN_stdfloat value); + void set_suspension_compression(PN_stdfloat value); + void set_suspension_damping(PN_stdfloat value); + void set_max_suspension_travel_cm(PN_stdfloat value); + void set_friction_slip(PN_stdfloat value); + void set_max_suspension_force(PN_stdfloat value); - INLINE PN_stdfloat get_suspension_stiffness() const; - INLINE PN_stdfloat get_suspension_compression() const; - INLINE PN_stdfloat get_suspension_damping() const; - INLINE PN_stdfloat get_max_suspension_travel_cm() const; - INLINE PN_stdfloat get_friction_slip() const; - INLINE PN_stdfloat get_max_suspension_force() const; + PN_stdfloat get_suspension_stiffness() const; + PN_stdfloat get_suspension_compression() const; + PN_stdfloat get_suspension_damping() const; + PN_stdfloat get_max_suspension_travel_cm() const; + PN_stdfloat get_friction_slip() const; + PN_stdfloat get_max_suspension_force() const; MAKE_PROPERTY(suspension_stiffness, get_suspension_stiffness, set_suspension_stiffness); MAKE_PROPERTY(suspension_compression, get_suspension_compression, set_suspension_compression); @@ -87,7 +87,7 @@ PUBLISHED: // Wheels BulletWheel create_wheel(); - INLINE int get_num_wheels() const; + int get_num_wheels() const; BulletWheel get_wheel(int idx) const; MAKE_SEQ(get_wheels, get_num_wheels, get_wheel); @@ -102,8 +102,9 @@ PUBLISHED: public: INLINE btRaycastVehicle *get_vehicle() const; + BulletRigidBodyNode *do_get_chassis(); - void sync_b2p(); + void do_sync_b2p(); private: btRaycastVehicle *_vehicle; diff --git a/panda/src/bullet/bulletWheel.I b/panda/src/bullet/bulletWheel.I index ed724e184b..cbf82a40bc 100644 --- a/panda/src/bullet/bulletWheel.I +++ b/panda/src/bullet/bulletWheel.I @@ -40,74 +40,3 @@ empty() { return BulletWheel(info); } -/** - * - */ -INLINE bool BulletWheelRaycastInfo:: -is_in_contact() const { - - return _info.m_isInContact; -} - -/** - * - */ -INLINE PN_stdfloat BulletWheelRaycastInfo:: -get_suspension_length() const { - - return _info.m_suspensionLength; -} - -/** - * - */ -INLINE LPoint3 BulletWheelRaycastInfo:: -get_contact_point_ws() const { - - return btVector3_to_LPoint3(_info.m_contactPointWS); -} - -/** - * - */ -INLINE LPoint3 BulletWheelRaycastInfo:: -get_hard_point_ws() const { - - return btVector3_to_LPoint3(_info.m_hardPointWS); -} - -/** - * - */ -INLINE LVector3 BulletWheelRaycastInfo:: -get_contact_normal_ws() const { - - return btVector3_to_LVector3(_info.m_contactNormalWS); -} - -/** - * - */ -INLINE LVector3 BulletWheelRaycastInfo:: -get_wheel_direction_ws() const { - - return btVector3_to_LVector3(_info.m_wheelDirectionWS); -} - -/** - * - */ -INLINE LVector3 BulletWheelRaycastInfo:: -get_wheel_axle_ws() const { - - return btVector3_to_LVector3(_info.m_wheelAxleWS); -} - -/** - * - */ -INLINE PandaNode *BulletWheelRaycastInfo:: -get_ground_object() const { - - return _info.m_groundObject ? (PandaNode *)_info.m_groundObject : NULL; -} diff --git a/panda/src/bullet/bulletWheel.cxx b/panda/src/bullet/bulletWheel.cxx index 687e5ea892..149dbd8bf5 100644 --- a/panda/src/bullet/bulletWheel.cxx +++ b/panda/src/bullet/bulletWheel.cxx @@ -34,6 +34,7 @@ BulletWheelRaycastInfo(btWheelInfo::RaycastInfo &info) : _info(info) { */ BulletWheelRaycastInfo BulletWheel:: get_raycast_info() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return BulletWheelRaycastInfo(_info.m_raycastInfo); } @@ -43,6 +44,7 @@ get_raycast_info() const { */ PN_stdfloat BulletWheel:: get_suspension_rest_length() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.getSuspensionRestLength(); } @@ -52,6 +54,7 @@ get_suspension_rest_length() const { */ void BulletWheel:: set_suspension_stiffness(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_suspensionStiffness = (btScalar)value; } @@ -61,6 +64,7 @@ set_suspension_stiffness(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_suspension_stiffness() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_suspensionStiffness; } @@ -71,6 +75,7 @@ get_suspension_stiffness() const { */ void BulletWheel:: set_max_suspension_travel_cm(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_maxSuspensionTravelCm = (btScalar)value; } @@ -80,6 +85,7 @@ set_max_suspension_travel_cm(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_max_suspension_travel_cm() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_maxSuspensionTravelCm; } @@ -89,6 +95,7 @@ get_max_suspension_travel_cm() const { */ void BulletWheel:: set_friction_slip(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_frictionSlip = (btScalar)value; } @@ -98,6 +105,7 @@ set_friction_slip(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_friction_slip() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_frictionSlip; } @@ -107,6 +115,7 @@ get_friction_slip() const { */ void BulletWheel:: set_max_suspension_force(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_maxSuspensionForce = (btScalar)value; } @@ -116,6 +125,7 @@ set_max_suspension_force(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_max_suspension_force() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_maxSuspensionForce; } @@ -125,6 +135,7 @@ get_max_suspension_force() const { */ void BulletWheel:: set_wheels_damping_compression(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_wheelsDampingCompression = (btScalar)value; } @@ -134,6 +145,7 @@ set_wheels_damping_compression(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_wheels_damping_compression() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_wheelsDampingCompression; } @@ -143,6 +155,7 @@ get_wheels_damping_compression() const { */ void BulletWheel:: set_wheels_damping_relaxation(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_wheelsDampingRelaxation = (btScalar)value; } @@ -152,6 +165,7 @@ set_wheels_damping_relaxation(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_wheels_damping_relaxation() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_wheelsDampingRelaxation; } @@ -164,6 +178,7 @@ get_wheels_damping_relaxation() const { */ void BulletWheel:: set_roll_influence(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_rollInfluence = (btScalar)value; } @@ -174,6 +189,7 @@ set_roll_influence(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_roll_influence() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_rollInfluence; } @@ -183,6 +199,7 @@ get_roll_influence() const { */ void BulletWheel:: set_wheel_radius(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_wheelsRadius = (btScalar)value; } @@ -192,6 +209,7 @@ set_wheel_radius(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_wheel_radius() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_wheelsRadius; } @@ -201,6 +219,7 @@ get_wheel_radius() const { */ void BulletWheel:: set_steering(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_steering = (btScalar)value; } @@ -210,6 +229,7 @@ set_steering(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_steering() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_steering; } @@ -219,6 +239,7 @@ get_steering() const { */ void BulletWheel:: set_rotation(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_rotation = (btScalar)value; } @@ -228,6 +249,7 @@ set_rotation(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_rotation() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_rotation; } @@ -237,6 +259,7 @@ get_rotation() const { */ void BulletWheel:: set_delta_rotation(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_deltaRotation = (btScalar)value; } @@ -246,6 +269,7 @@ set_delta_rotation(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_delta_rotation() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_deltaRotation; } @@ -255,6 +279,7 @@ get_delta_rotation() const { */ void BulletWheel:: set_engine_force(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_engineForce = (btScalar)value; } @@ -264,6 +289,7 @@ set_engine_force(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_engine_force() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_engineForce; } @@ -273,6 +299,7 @@ get_engine_force() const { */ void BulletWheel:: set_brake(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_brake = (btScalar)value; } @@ -282,6 +309,7 @@ set_brake(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_brake() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_brake; } @@ -291,6 +319,7 @@ get_brake() const { */ void BulletWheel:: set_skid_info(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_skidInfo = (btScalar)value; } @@ -300,6 +329,7 @@ set_skid_info(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_skid_info() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_skidInfo; } @@ -309,6 +339,7 @@ get_skid_info() const { */ void BulletWheel:: set_wheels_suspension_force(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_wheelsSuspensionForce = (btScalar)value; } @@ -318,6 +349,7 @@ set_wheels_suspension_force(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_wheels_suspension_force() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_wheelsSuspensionForce; } @@ -327,6 +359,7 @@ get_wheels_suspension_force() const { */ void BulletWheel:: set_suspension_relative_velocity(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_suspensionRelativeVelocity = (btScalar)value; } @@ -336,6 +369,7 @@ set_suspension_relative_velocity(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_suspension_relative_velocity() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_suspensionRelativeVelocity; } @@ -345,6 +379,7 @@ get_suspension_relative_velocity() const { */ void BulletWheel:: set_clipped_inv_connection_point_cs(PN_stdfloat value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_clippedInvContactDotSuspension = (btScalar)value; } @@ -354,6 +389,7 @@ set_clipped_inv_connection_point_cs(PN_stdfloat value) { */ PN_stdfloat BulletWheel:: get_clipped_inv_connection_point_cs() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (PN_stdfloat)_info.m_clippedInvContactDotSuspension; } @@ -363,6 +399,7 @@ get_clipped_inv_connection_point_cs() const { */ void BulletWheel:: set_chassis_connection_point_cs(const LPoint3 &pos) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!pos.is_nan()); _info.m_chassisConnectionPointCS = LVecBase3_to_btVector3(pos); @@ -373,6 +410,7 @@ set_chassis_connection_point_cs(const LPoint3 &pos) { */ LPoint3 BulletWheel:: get_chassis_connection_point_cs() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LPoint3(_info.m_chassisConnectionPointCS); } @@ -383,6 +421,7 @@ get_chassis_connection_point_cs() const { */ void BulletWheel:: set_wheel_direction_cs(const LVector3 &dir) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!dir.is_nan()); _info.m_wheelDirectionCS = LVecBase3_to_btVector3(dir); @@ -393,6 +432,7 @@ set_wheel_direction_cs(const LVector3 &dir) { */ LVector3 BulletWheel:: get_wheel_direction_cs() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_info.m_wheelDirectionCS); } @@ -402,6 +442,7 @@ get_wheel_direction_cs() const { */ void BulletWheel:: set_wheel_axle_cs(const LVector3 &axle) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!axle.is_nan()); _info.m_wheelAxleCS = LVecBase3_to_btVector3(axle); @@ -412,6 +453,7 @@ set_wheel_axle_cs(const LVector3 &axle) { */ LVector3 BulletWheel:: get_wheel_axle_cs() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btVector3_to_LVector3(_info.m_wheelAxleCS); } @@ -421,6 +463,7 @@ get_wheel_axle_cs() const { */ void BulletWheel:: set_world_transform(const LMatrix4 &mat) { + LightMutexHolder holder(BulletWorld::get_global_lock()); nassertv(!mat.is_nan()); _info.m_worldTransform = LMatrix4_to_btTrans(mat); @@ -431,6 +474,7 @@ set_world_transform(const LMatrix4 &mat) { */ LMatrix4 BulletWheel:: get_world_transform() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return btTrans_to_LMatrix4(_info.m_worldTransform); } @@ -440,6 +484,7 @@ get_world_transform() const { */ void BulletWheel:: set_front_wheel(bool value) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_bIsFrontWheel = value; } @@ -449,6 +494,7 @@ set_front_wheel(bool value) { */ bool BulletWheel:: is_front_wheel() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return _info.m_bIsFrontWheel; } @@ -458,6 +504,7 @@ is_front_wheel() const { */ void BulletWheel:: set_node(PandaNode *node) { + LightMutexHolder holder(BulletWorld::get_global_lock()); _info.m_clientInfo = (void *)node; } @@ -468,6 +515,87 @@ set_node(PandaNode *node) { */ PandaNode *BulletWheel:: get_node() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); return (_info.m_clientInfo == NULL) ? NULL : (PandaNode *)_info.m_clientInfo; } + +/** + * + */ +bool BulletWheelRaycastInfo:: +is_in_contact() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _info.m_isInContact; +} + +/** + * + */ +PN_stdfloat BulletWheelRaycastInfo:: +get_suspension_length() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _info.m_suspensionLength; +} + +/** + * + */ +LPoint3 BulletWheelRaycastInfo:: +get_contact_point_ws() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LPoint3(_info.m_contactPointWS); +} + +/** + * + */ +LPoint3 BulletWheelRaycastInfo:: +get_hard_point_ws() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LPoint3(_info.m_hardPointWS); +} + +/** + * + */ +LVector3 BulletWheelRaycastInfo:: +get_contact_normal_ws() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_info.m_contactNormalWS); +} + +/** + * + */ +LVector3 BulletWheelRaycastInfo:: +get_wheel_direction_ws() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_info.m_wheelDirectionWS); +} + +/** + * + */ +LVector3 BulletWheelRaycastInfo:: +get_wheel_axle_ws() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return btVector3_to_LVector3(_info.m_wheelAxleWS); +} + +/** + * + */ +PandaNode *BulletWheelRaycastInfo:: +get_ground_object() const { + LightMutexHolder holder(BulletWorld::get_global_lock()); + + return _info.m_groundObject ? (PandaNode *)_info.m_groundObject : NULL; +} diff --git a/panda/src/bullet/bulletWheel.h b/panda/src/bullet/bulletWheel.h index 2b6ecd8a80..414ecd94f3 100644 --- a/panda/src/bullet/bulletWheel.h +++ b/panda/src/bullet/bulletWheel.h @@ -30,14 +30,14 @@ class EXPCL_PANDABULLET BulletWheelRaycastInfo { PUBLISHED: INLINE ~BulletWheelRaycastInfo(); - INLINE bool is_in_contact() const; - INLINE PN_stdfloat get_suspension_length() const; - INLINE LVector3 get_contact_normal_ws() const; - INLINE LVector3 get_wheel_direction_ws() const; - INLINE LVector3 get_wheel_axle_ws() const; - INLINE LPoint3 get_contact_point_ws() const; - INLINE LPoint3 get_hard_point_ws() const; - INLINE PandaNode *get_ground_object() const; + bool is_in_contact() const; + PN_stdfloat get_suspension_length() const; + LVector3 get_contact_normal_ws() const; + LVector3 get_wheel_direction_ws() const; + LVector3 get_wheel_axle_ws() const; + LPoint3 get_contact_point_ws() const; + LPoint3 get_hard_point_ws() const; + PandaNode *get_ground_object() const; MAKE_PROPERTY(in_contact, is_in_contact); MAKE_PROPERTY(suspension_length, get_suspension_length); diff --git a/panda/src/bullet/bulletWorld.I b/panda/src/bullet/bulletWorld.I index c2ecd2cec3..61893a1531 100644 --- a/panda/src/bullet/bulletWorld.I +++ b/panda/src/bullet/bulletWorld.I @@ -50,19 +50,6 @@ INLINE BulletWorld:: delete _broadphase; } -/** - * - */ -INLINE void BulletWorld:: -set_debug_node(BulletDebugNode *node) { - nassertv(node); - if (node != _debug) { - clear_debug_node(); - _debug = node; - _world->setDebugDrawer(&(_debug->_drawer)); - } -} - /** * */ @@ -108,125 +95,3 @@ get_dispatcher() const { return _dispatcher; } -/** - * - */ -INLINE int BulletWorld:: -get_num_rigid_bodies() const { - - return _bodies.size(); -} - -/** - * - */ -INLINE BulletRigidBodyNode *BulletWorld:: -get_rigid_body(int idx) const { - - nassertr(idx >= 0 && idx < (int)_bodies.size(), NULL); - return _bodies[idx]; -} - -/** - * - */ -INLINE int BulletWorld:: -get_num_soft_bodies() const { - - return _softbodies.size(); -} - -/** - * - */ -INLINE BulletSoftBodyNode *BulletWorld:: -get_soft_body(int idx) const { - - nassertr(idx >= 0 && idx < (int)_softbodies.size(), NULL); - return _softbodies[idx]; -} - -/** - * - */ -INLINE int BulletWorld:: -get_num_ghosts() const { - - return _ghosts.size(); -} - -/** - * - */ -INLINE BulletGhostNode *BulletWorld:: -get_ghost(int idx) const { - - nassertr(idx >= 0 && idx < (int)_ghosts.size(), NULL); - return _ghosts[idx]; -} - -/** - * - */ -INLINE int BulletWorld:: -get_num_characters() const { - - return _characters.size(); -} - -/** - * - */ -INLINE BulletBaseCharacterControllerNode *BulletWorld:: -get_character(int idx) const { - - nassertr(idx >= 0 && idx < (int)_characters.size(), NULL); - return _characters[idx]; -} - -/** - * - */ -INLINE int BulletWorld:: -get_num_vehicles() const { - - return _vehicles.size(); -} - -/** - * - */ -INLINE BulletVehicle *BulletWorld:: -get_vehicle(int idx) const { - - nassertr(idx >= 0 && idx < (int)_vehicles.size(), NULL); - return _vehicles[idx]; -} - -/** - * - */ -INLINE int BulletWorld:: -get_num_constraints() const { - - return _constraints.size(); -} - -/** - * - */ -INLINE BulletConstraint *BulletWorld:: -get_constraint(int idx) const { - - nassertr(idx >= 0 && idx < (int)_constraints.size(), NULL); - return _constraints[idx]; -} - -/** - * - */ -INLINE int BulletWorld:: -get_num_manifolds() const { - - return _world->getDispatcher()->getNumManifolds(); -} diff --git a/panda/src/bullet/bulletWorld.cxx b/panda/src/bullet/bulletWorld.cxx index d60339f360..f3d48e3c74 100644 --- a/panda/src/bullet/bulletWorld.cxx +++ b/panda/src/bullet/bulletWorld.cxx @@ -118,6 +118,17 @@ BulletWorld() { _world->getSolverInfo().m_numIterations = bullet_solver_iterations; } +/** + * + */ +LightMutex &BulletWorld:: +get_global_lock() { + + static LightMutex lock; + + return lock; +} + /** * */ @@ -127,13 +138,33 @@ get_world_info() { return BulletSoftBodyWorldInfo(_info); } +/** + * + */ +void BulletWorld:: +set_debug_node(BulletDebugNode *node) { + LightMutexHolder holder(get_global_lock()); + + nassertv(node); + if (node != _debug) { + if (_debug != nullptr) { + _debug->_debug_stale = false; + _debug->_debug_world = nullptr; + } + + _debug = node; + _world->setDebugDrawer(&(_debug->_drawer)); + } +} + /** * Removes a debug node that has been assigned to this BulletWorld. */ void BulletWorld:: clear_debug_node() { + LightMutexHolder holder(get_global_lock()); + if (_debug != nullptr) { - LightMutexHolder holder(_debug->_lock); _debug->_debug_stale = false; _debug->_debug_world = nullptr; _world->setDebugDrawer(nullptr); @@ -146,6 +177,7 @@ clear_debug_node() { */ void BulletWorld:: set_gravity(const LVector3 &gravity) { + LightMutexHolder holder(get_global_lock()); _world->setGravity(LVecBase3_to_btVector3(gravity)); _info.m_gravity.setValue(gravity.get_x(), gravity.get_y(), gravity.get_z()); @@ -156,6 +188,7 @@ set_gravity(const LVector3 &gravity) { */ void BulletWorld:: set_gravity(PN_stdfloat gx, PN_stdfloat gy, PN_stdfloat gz) { + LightMutexHolder holder(get_global_lock()); _world->setGravity(btVector3((btScalar)gx, (btScalar)gy, (btScalar)gz)); _info.m_gravity.setValue((btScalar)gx, (btScalar)gy, (btScalar)gz); @@ -166,6 +199,7 @@ set_gravity(PN_stdfloat gx, PN_stdfloat gy, PN_stdfloat gz) { */ const LVector3 BulletWorld:: get_gravity() const { + LightMutexHolder holder(get_global_lock()); return btVector3_to_LVector3(_world->getGravity()); } @@ -175,6 +209,7 @@ get_gravity() const { */ int BulletWorld:: do_physics(PN_stdfloat dt, int max_substeps, PN_stdfloat stepsize) { + LightMutexHolder holder(get_global_lock()); _pstat_physics.start(); @@ -182,7 +217,7 @@ do_physics(PN_stdfloat dt, int max_substeps, PN_stdfloat stepsize) { // Synchronize Panda to Bullet _pstat_p2b.start(); - sync_p2b(dt, num_substeps); + do_sync_p2b(dt, num_substeps); _pstat_p2b.stop(); // Simulation @@ -192,13 +227,13 @@ do_physics(PN_stdfloat dt, int max_substeps, PN_stdfloat stepsize) { // Synchronize Bullet to Panda _pstat_b2p.start(); - sync_b2p(); + do_sync_b2p(); _info.m_sparsesdf.GarbageCollect(bullet_gc_lifetime); _pstat_b2p.stop(); // Render debug if (_debug) { - _debug->sync_b2p(_world); + _debug->do_sync_b2p(_world); } _pstat_physics.stop(); @@ -207,52 +242,52 @@ do_physics(PN_stdfloat dt, int max_substeps, PN_stdfloat stepsize) { } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -sync_p2b(PN_stdfloat dt, int num_substeps) { +do_sync_p2b(PN_stdfloat dt, int num_substeps) { - for (int i=0; i < get_num_rigid_bodies(); i++) { - get_rigid_body(i)->sync_p2b(); + for (int i=0; i < _bodies.size(); i++) { + _bodies[i]->do_sync_p2b(); } - for (int i=0; i < get_num_soft_bodies(); i++) { - get_soft_body(i)->sync_p2b(); + for (int i=0; i < _softbodies.size(); i++) { + _softbodies[i]->do_sync_p2b(); } - for (int i=0; i < get_num_ghosts(); i++) { - get_ghost(i)->sync_p2b(); + for (int i=0; i < _ghosts.size(); i++) { + _ghosts[i]->do_sync_p2b(); } - for (int i=0; i < get_num_characters(); i++) { - get_character(i)->sync_p2b(dt, num_substeps); + for (int i=0; i < _characters.size(); i++) { + _characters[i]->do_sync_p2b(dt, num_substeps); } } /** - * + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -sync_b2p() { +do_sync_b2p() { - for (int i=0; i < get_num_vehicles(); i++) { - get_vehicle(i)->sync_b2p(); + for (int i=0; i < _vehicles.size(); i++) { + _vehicles[i]->do_sync_b2p(); } - for (int i=0; i < get_num_rigid_bodies(); i++) { - get_rigid_body(i)->sync_b2p(); + for (int i=0; i < _bodies.size(); i++) { + _bodies[i]->do_sync_b2p(); } - for (int i=0; i < get_num_soft_bodies(); i++) { - get_soft_body(i)->sync_b2p(); + for (int i=0; i < _softbodies.size(); i++) { + _softbodies[i]->do_sync_b2p(); } - for (int i=0; i < get_num_ghosts(); i++) { - get_ghost(i)->sync_b2p(); + for (int i=0; i < _ghosts.size(); i++) { + _ghosts[i]->do_sync_b2p(); } - for (int i=0; i < get_num_characters(); i++) { - get_character(i)->sync_b2p(); + for (int i=0; i < _characters.size(); i++) { + _characters[i]->do_sync_b2p(); } } @@ -261,24 +296,25 @@ sync_b2p() { */ void BulletWorld:: attach(TypedObject *object) { + LightMutexHolder holder(get_global_lock()); if (object->is_of_type(BulletGhostNode::get_class_type())) { - attach_ghost(DCAST(BulletGhostNode, object)); + do_attach_ghost(DCAST(BulletGhostNode, object)); } else if (object->is_of_type(BulletRigidBodyNode::get_class_type())) { - attach_rigid_body(DCAST(BulletRigidBodyNode, object)); + do_attach_rigid_body(DCAST(BulletRigidBodyNode, object)); } else if (object->is_of_type(BulletSoftBodyNode::get_class_type())) { - attach_soft_body(DCAST(BulletSoftBodyNode, object)); + do_attach_soft_body(DCAST(BulletSoftBodyNode, object)); } else if (object->is_of_type(BulletBaseCharacterControllerNode::get_class_type())) { - attach_character(DCAST(BulletBaseCharacterControllerNode, object)); + do_attach_character(DCAST(BulletBaseCharacterControllerNode, object)); } else if (object->is_of_type(BulletVehicle::get_class_type())) { - attach_vehicle(DCAST(BulletVehicle, object)); + do_attach_vehicle(DCAST(BulletVehicle, object)); } else if (object->is_of_type(BulletConstraint::get_class_type())) { - attach_constraint(DCAST(BulletConstraint, object)); + do_attach_constraint(DCAST(BulletConstraint, object)); } else { bullet_cat->error() << "not a bullet world object!" << endl; @@ -290,24 +326,25 @@ attach(TypedObject *object) { */ void BulletWorld:: remove(TypedObject *object) { + LightMutexHolder holder(get_global_lock()); if (object->is_of_type(BulletGhostNode::get_class_type())) { - remove_ghost(DCAST(BulletGhostNode, object)); + do_remove_ghost(DCAST(BulletGhostNode, object)); } else if (object->is_of_type(BulletRigidBodyNode::get_class_type())) { - remove_rigid_body(DCAST(BulletRigidBodyNode, object)); + do_remove_rigid_body(DCAST(BulletRigidBodyNode, object)); } else if (object->is_of_type(BulletSoftBodyNode::get_class_type())) { - remove_soft_body(DCAST(BulletSoftBodyNode, object)); + do_remove_soft_body(DCAST(BulletSoftBodyNode, object)); } else if (object->is_of_type(BulletBaseCharacterControllerNode::get_class_type())) { - remove_character(DCAST(BulletBaseCharacterControllerNode, object)); + do_remove_character(DCAST(BulletBaseCharacterControllerNode, object)); } else if (object->is_of_type(BulletVehicle::get_class_type())) { - remove_vehicle(DCAST(BulletVehicle, object)); + do_remove_vehicle(DCAST(BulletVehicle, object)); } else if (object->is_of_type(BulletConstraint::get_class_type())) { - remove_constraint(DCAST(BulletConstraint, object)); + do_remove_constraint(DCAST(BulletConstraint, object)); } else { bullet_cat->error() << "not a bullet world object!" << endl; @@ -319,6 +356,127 @@ remove(TypedObject *object) { */ void BulletWorld:: attach_rigid_body(BulletRigidBodyNode *node) { + LightMutexHolder holder(get_global_lock()); + + do_attach_rigid_body(node); +} + +/** + * Deprecated.! Please use BulletWorld::remove + */ +void BulletWorld:: +remove_rigid_body(BulletRigidBodyNode *node) { + LightMutexHolder holder(get_global_lock()); + + do_remove_rigid_body(node); +} + +/** + * Deprecated! Please use BulletWorld::attach + */ +void BulletWorld:: +attach_soft_body(BulletSoftBodyNode *node) { + LightMutexHolder holder(get_global_lock()); + + do_attach_soft_body(node); +} + +/** + * Deprecated.! Please use BulletWorld::remove + */ +void BulletWorld:: +remove_soft_body(BulletSoftBodyNode *node) { + LightMutexHolder holder(get_global_lock()); + + do_remove_soft_body(node); +} + +/** + * Deprecated! Please use BulletWorld::attach + */ +void BulletWorld:: +attach_ghost(BulletGhostNode *node) { + LightMutexHolder holder(get_global_lock()); + + do_attach_ghost(node); +} + +/** + * Deprecated.! Please use BulletWorld::remove + */ +void BulletWorld:: +remove_ghost(BulletGhostNode *node) { + LightMutexHolder holder(get_global_lock()); + + do_remove_ghost(node); +} + +/** + * Deprecated! Please use BulletWorld::attach + */ +void BulletWorld:: +attach_character(BulletBaseCharacterControllerNode *node) { + LightMutexHolder holder(get_global_lock()); + + do_attach_character(node); +} + +/** + * Deprecated.! Please use BulletWorld::remove + */ +void BulletWorld:: +remove_character(BulletBaseCharacterControllerNode *node) { + LightMutexHolder holder(get_global_lock()); + + do_remove_character(node); +} + +/** + * Deprecated! Please use BulletWorld::attach + */ +void BulletWorld:: +attach_vehicle(BulletVehicle *vehicle) { + LightMutexHolder holder(get_global_lock()); + + do_attach_vehicle(vehicle); +} + +/** + * Deprecated.! Please use BulletWorld::remove + */ +void BulletWorld:: +remove_vehicle(BulletVehicle *vehicle) { + LightMutexHolder holder(get_global_lock()); + + do_remove_vehicle(vehicle); +} + +/** + * Attaches a single constraint to a world. Collision checks between the + * linked objects will be disabled if the second parameter is set to TRUE. + */ +void BulletWorld:: +attach_constraint(BulletConstraint *constraint, bool linked_collision) { + LightMutexHolder holder(get_global_lock()); + + do_attach_constraint(constraint, linked_collision); +} + +/** + * Deprecated.! Please use BulletWorld::remove + */ +void BulletWorld:: +remove_constraint(BulletConstraint *constraint) { + LightMutexHolder holder(get_global_lock()); + + do_remove_constraint(constraint); +} + +/** + * Assumes the lock(bullet global lock) is held by the caller + */ +void BulletWorld:: +do_attach_rigid_body(BulletRigidBodyNode *node) { nassertv(node); @@ -338,10 +496,10 @@ attach_rigid_body(BulletRigidBodyNode *node) { } /** - * Deprecated.! Please use BulletWorld::remove + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -remove_rigid_body(BulletRigidBodyNode *node) { +do_remove_rigid_body(BulletRigidBodyNode *node) { nassertv(node); @@ -361,10 +519,10 @@ remove_rigid_body(BulletRigidBodyNode *node) { } /** - * Deprecated! Please use BulletWorld::attach + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -attach_soft_body(BulletSoftBodyNode *node) { +do_attach_soft_body(BulletSoftBodyNode *node) { nassertv(node); @@ -388,10 +546,10 @@ attach_soft_body(BulletSoftBodyNode *node) { } /** - * Deprecated.! Please use BulletWorld::remove + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -remove_soft_body(BulletSoftBodyNode *node) { +do_remove_soft_body(BulletSoftBodyNode *node) { nassertv(node); @@ -411,10 +569,10 @@ remove_soft_body(BulletSoftBodyNode *node) { } /** - * Deprecated! Please use BulletWorld::attach + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -attach_ghost(BulletGhostNode *node) { +do_attach_ghost(BulletGhostNode *node) { nassertv(node); @@ -452,10 +610,10 @@ enum CollisionFilterGroups { } /** - * Deprecated.! Please use BulletWorld::remove + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -remove_ghost(BulletGhostNode *node) { +do_remove_ghost(BulletGhostNode *node) { nassertv(node); @@ -475,10 +633,10 @@ remove_ghost(BulletGhostNode *node) { } /** - * Deprecated! Please use BulletWorld::attach + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -attach_character(BulletBaseCharacterControllerNode *node) { +do_attach_character(BulletBaseCharacterControllerNode *node) { nassertv(node); @@ -501,10 +659,10 @@ attach_character(BulletBaseCharacterControllerNode *node) { } /** - * Deprecated.! Please use BulletWorld::remove + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -remove_character(BulletBaseCharacterControllerNode *node) { +do_remove_character(BulletBaseCharacterControllerNode *node) { nassertv(node); @@ -523,10 +681,10 @@ remove_character(BulletBaseCharacterControllerNode *node) { } /** - * Deprecated! Please use BulletWorld::attach + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -attach_vehicle(BulletVehicle *vehicle) { +do_attach_vehicle(BulletVehicle *vehicle) { nassertv(vehicle); @@ -544,14 +702,14 @@ attach_vehicle(BulletVehicle *vehicle) { } /** - * Deprecated.! Please use BulletWorld::remove + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -remove_vehicle(BulletVehicle *vehicle) { +do_remove_vehicle(BulletVehicle *vehicle) { nassertv(vehicle); - remove_rigid_body(vehicle->get_chassis()); + do_remove_rigid_body(vehicle->do_get_chassis()); BulletVehicles::iterator found; PT(BulletVehicle) ptvehicle = vehicle; @@ -569,9 +727,10 @@ remove_vehicle(BulletVehicle *vehicle) { /** * Attaches a single constraint to a world. Collision checks between the * linked objects will be disabled if the second parameter is set to TRUE. + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -attach_constraint(BulletConstraint *constraint, bool linked_collision) { +do_attach_constraint(BulletConstraint *constraint, bool linked_collision) { nassertv(constraint); @@ -589,10 +748,10 @@ attach_constraint(BulletConstraint *constraint, bool linked_collision) { } /** - * Deprecated.! Please use BulletWorld::remove + * Assumes the lock(bullet global lock) is held by the caller */ void BulletWorld:: -remove_constraint(BulletConstraint *constraint) { +do_remove_constraint(BulletConstraint *constraint) { nassertv(constraint); @@ -609,11 +768,148 @@ remove_constraint(BulletConstraint *constraint) { } } +/** + * + */ +int BulletWorld:: +get_num_rigid_bodies() const { + LightMutexHolder holder(get_global_lock()); + + return _bodies.size(); +} + +/** + * + */ +BulletRigidBodyNode *BulletWorld:: +get_rigid_body(int idx) const { + LightMutexHolder holder(get_global_lock()); + + nassertr(idx >= 0 && idx < (int)_bodies.size(), NULL); + return _bodies[idx]; +} + +/** + * + */ +int BulletWorld:: +get_num_soft_bodies() const { + LightMutexHolder holder(get_global_lock()); + + return _softbodies.size(); +} + +/** + * + */ +BulletSoftBodyNode *BulletWorld:: +get_soft_body(int idx) const { + LightMutexHolder holder(get_global_lock()); + + nassertr(idx >= 0 && idx < (int)_softbodies.size(), NULL); + return _softbodies[idx]; +} + +/** + * + */ +int BulletWorld:: +get_num_ghosts() const { + LightMutexHolder holder(get_global_lock()); + + return _ghosts.size(); +} + +/** + * + */ +BulletGhostNode *BulletWorld:: +get_ghost(int idx) const { + LightMutexHolder holder(get_global_lock()); + + nassertr(idx >= 0 && idx < (int)_ghosts.size(), NULL); + return _ghosts[idx]; +} + +/** + * + */ +int BulletWorld:: +get_num_characters() const { + LightMutexHolder holder(get_global_lock()); + + return _characters.size(); +} + +/** + * + */ +BulletBaseCharacterControllerNode *BulletWorld:: +get_character(int idx) const { + LightMutexHolder holder(get_global_lock()); + + nassertr(idx >= 0 && idx < (int)_characters.size(), NULL); + return _characters[idx]; +} + +/** + * + */ +int BulletWorld:: +get_num_vehicles() const { + LightMutexHolder holder(get_global_lock()); + + return _vehicles.size(); +} + +/** + * + */ +BulletVehicle *BulletWorld:: +get_vehicle(int idx) const { + LightMutexHolder holder(get_global_lock()); + + nassertr(idx >= 0 && idx < (int)_vehicles.size(), NULL); + return _vehicles[idx]; +} + +/** + * + */ +int BulletWorld:: +get_num_constraints() const { + LightMutexHolder holder(get_global_lock()); + + return _constraints.size(); +} + +/** + * + */ +BulletConstraint *BulletWorld:: +get_constraint(int idx) const { + LightMutexHolder holder(get_global_lock()); + + nassertr(idx >= 0 && idx < (int)_constraints.size(), NULL); + return _constraints[idx]; +} + +/** + * + */ +int BulletWorld:: +get_num_manifolds() const { + LightMutexHolder holder(get_global_lock()); + + return _world->getDispatcher()->getNumManifolds(); +} + /** * */ BulletClosestHitRayResult BulletWorld:: ray_test_closest(const LPoint3 &from_pos, const LPoint3 &to_pos, const CollideMask &mask) const { + LightMutexHolder holder(get_global_lock()); nassertr(!from_pos.is_nan(), BulletClosestHitRayResult::empty()); nassertr(!to_pos.is_nan(), BulletClosestHitRayResult::empty()); @@ -631,6 +927,7 @@ ray_test_closest(const LPoint3 &from_pos, const LPoint3 &to_pos, const CollideMa */ BulletAllHitsRayResult BulletWorld:: ray_test_all(const LPoint3 &from_pos, const LPoint3 &to_pos, const CollideMask &mask) const { + LightMutexHolder holder(get_global_lock()); nassertr(!from_pos.is_nan(), BulletAllHitsRayResult::empty()); nassertr(!to_pos.is_nan(), BulletAllHitsRayResult::empty()); @@ -648,13 +945,16 @@ ray_test_all(const LPoint3 &from_pos, const LPoint3 &to_pos, const CollideMask & */ BulletClosestHitSweepResult BulletWorld:: sweep_test_closest(BulletShape *shape, const TransformState &from_ts, const TransformState &to_ts, const CollideMask &mask, PN_stdfloat penetration) const { + LightMutexHolder holder(get_global_lock()); nassertr(shape, BulletClosestHitSweepResult::empty()); - nassertr(shape->is_convex(), BulletClosestHitSweepResult::empty()); + + const btConvexShape *convex = (const btConvexShape *) shape->ptr(); + nassertr(convex->isConvex(), BulletClosestHitSweepResult::empty()); + nassertr(!from_ts.is_invalid(), BulletClosestHitSweepResult::empty()); nassertr(!to_ts.is_invalid(), BulletClosestHitSweepResult::empty()); - const btConvexShape *convex = (const btConvexShape *) shape->ptr(); const btVector3 from_pos = LVecBase3_to_btVector3(from_ts.get_pos()); const btVector3 to_pos = LVecBase3_to_btVector3(to_ts.get_pos()); const btTransform from_trans = LMatrix4_to_btTrans(from_ts.get_mat()); @@ -671,6 +971,7 @@ sweep_test_closest(BulletShape *shape, const TransformState &from_ts, const Tran */ bool BulletWorld:: filter_test(PandaNode *node0, PandaNode *node1) const { + LightMutexHolder holder(get_global_lock()); nassertr(node0, false); nassertr(node1, false); @@ -702,6 +1003,7 @@ filter_test(PandaNode *node0, PandaNode *node1) const { */ BulletContactResult BulletWorld:: contact_test(PandaNode *node, bool use_filter) const { + LightMutexHolder holder(get_global_lock()); btCollisionObject *obj = get_collision_object(node); @@ -727,6 +1029,7 @@ contact_test(PandaNode *node, bool use_filter) const { */ BulletContactResult BulletWorld:: contact_test_pair(PandaNode *node0, PandaNode *node1) const { + LightMutexHolder holder(get_global_lock()); btCollisionObject *obj0 = get_collision_object(node0); btCollisionObject *obj1 = get_collision_object(node1); @@ -746,6 +1049,7 @@ contact_test_pair(PandaNode *node0, PandaNode *node1) const { */ BulletPersistentManifold *BulletWorld:: get_manifold(int idx) const { + LightMutexHolder holder(get_global_lock()); nassertr(idx < get_num_manifolds(), NULL); @@ -780,6 +1084,7 @@ get_collision_object(PandaNode *node) { */ void BulletWorld:: set_group_collision_flag(unsigned int group1, unsigned int group2, bool enable) { + LightMutexHolder holder(get_global_lock()); if (bullet_filter_algorithm != FA_groups_mask) { bullet_cat.warning() << "filter algorithm is not 'groups-mask'" << endl; @@ -794,6 +1099,7 @@ set_group_collision_flag(unsigned int group1, unsigned int group2, bool enable) */ bool BulletWorld:: get_group_collision_flag(unsigned int group1, unsigned int group2) const { + LightMutexHolder holder(get_global_lock()); return _filter_cb2._collide[group1].get_bit(group2); } @@ -803,6 +1109,7 @@ get_group_collision_flag(unsigned int group1, unsigned int group2) const { */ void BulletWorld:: set_contact_added_callback(CallbackObject *obj) { + LightMutexHolder holder(get_global_lock()); _world->getSolverInfo().m_solverMode |= SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION; _world->getSolverInfo().m_solverMode |= SOLVER_USE_2_FRICTION_DIRECTIONS; @@ -816,6 +1123,7 @@ set_contact_added_callback(CallbackObject *obj) { */ void BulletWorld:: clear_contact_added_callback() { + LightMutexHolder holder(get_global_lock()); _world->getSolverInfo().m_solverMode &= ~SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION; _world->getSolverInfo().m_solverMode &= ~SOLVER_USE_2_FRICTION_DIRECTIONS; @@ -829,6 +1137,7 @@ clear_contact_added_callback() { */ void BulletWorld:: set_tick_callback(CallbackObject *obj, bool is_pretick) { + LightMutexHolder holder(get_global_lock()); nassertv(obj != NULL); _tick_callback_obj = obj; @@ -840,6 +1149,7 @@ set_tick_callback(CallbackObject *obj, bool is_pretick) { */ void BulletWorld:: clear_tick_callback() { + LightMutexHolder holder(get_global_lock()); _tick_callback_obj = NULL; _world->setInternalTickCallback(NULL); @@ -866,6 +1176,7 @@ tick_callback(btDynamicsWorld *world, btScalar timestep) { */ void BulletWorld:: set_filter_callback(CallbackObject *obj) { + LightMutexHolder holder(get_global_lock()); nassertv(obj != NULL); @@ -881,6 +1192,7 @@ set_filter_callback(CallbackObject *obj) { */ void BulletWorld:: clear_filter_callback() { + LightMutexHolder holder(get_global_lock()); _filter_cb3._filter_callback_obj = NULL; } diff --git a/panda/src/bullet/bulletWorld.h b/panda/src/bullet/bulletWorld.h index 558d086b37..021b6ee268 100644 --- a/panda/src/bullet/bulletWorld.h +++ b/panda/src/bullet/bulletWorld.h @@ -37,6 +37,7 @@ #include "callbackObject.h" #include "collideMask.h" #include "luse.h" +#include "lightMutex.h" class BulletPersistentManifold; class BulletShape; @@ -62,48 +63,43 @@ PUBLISHED: BulletSoftBodyWorldInfo get_world_info(); // Debug - INLINE void set_debug_node(BulletDebugNode *node); + void set_debug_node(BulletDebugNode *node); void clear_debug_node(); INLINE BulletDebugNode *get_debug_node() const; INLINE bool has_debug_node() const; // AttachRemove void attach(TypedObject *object); + void remove(TypedObject *object); void attach_constraint(BulletConstraint *constraint, bool linked_collision=false); - void remove(TypedObject *object); - // Ghost object - INLINE int get_num_ghosts() const; - INLINE BulletGhostNode *get_ghost(int idx) const; + int get_num_ghosts() const; + BulletGhostNode *get_ghost(int idx) const; MAKE_SEQ(get_ghosts, get_num_ghosts, get_ghost); // Rigid body - INLINE int get_num_rigid_bodies() const; - INLINE BulletRigidBodyNode *get_rigid_body(int idx) const; + int get_num_rigid_bodies() const; + BulletRigidBodyNode *get_rigid_body(int idx) const; MAKE_SEQ(get_rigid_bodies, get_num_rigid_bodies, get_rigid_body); // Soft body - INLINE int get_num_soft_bodies() const; - INLINE BulletSoftBodyNode *get_soft_body(int idx) const; + int get_num_soft_bodies() const; + BulletSoftBodyNode *get_soft_body(int idx) const; MAKE_SEQ(get_soft_bodies, get_num_soft_bodies, get_soft_body); // Character controller - INLINE int get_num_characters() const; - INLINE BulletBaseCharacterControllerNode *get_character(int idx) const; + int get_num_characters() const; + BulletBaseCharacterControllerNode *get_character(int idx) const; MAKE_SEQ(get_characters, get_num_characters, get_character); - // Vehicle - void attach_vehicle(BulletVehicle *vehicle); - void remove_vehicle(BulletVehicle *vehicle); - - INLINE int get_num_vehicles() const; - INLINE BulletVehicle *get_vehicle(int idx) const; + int get_num_vehicles() const; + BulletVehicle *get_vehicle(int idx) const; MAKE_SEQ(get_vehicles, get_num_vehicles, get_vehicle); // Constraint - INLINE int get_num_constraints() const; - INLINE BulletConstraint *get_constraint(int idx) const; + int get_num_constraints() const; + BulletConstraint *get_constraint(int idx) const; MAKE_SEQ(get_constraints, get_num_constraints, get_constraint); // Raycast and other queries @@ -130,7 +126,7 @@ PUBLISHED: bool filter_test(PandaNode *node0, PandaNode *node1) const; // Manifolds - INLINE int get_num_manifolds() const; + int get_num_manifolds() const; BulletPersistentManifold *get_manifold(int idx) const; MAKE_SEQ(get_manifolds, get_num_manifolds, get_manifold); @@ -171,7 +167,7 @@ PUBLISHED: MAKE_SEQ_PROPERTY(constraints, get_num_constraints, get_constraint); MAKE_SEQ_PROPERTY(manifolds, get_num_manifolds, get_manifold); -PUBLISHED: // Deprecated methods, will become private soon +PUBLISHED: // Deprecated methods, will be removed soon void attach_ghost(BulletGhostNode *node); void remove_ghost(BulletGhostNode *node); @@ -184,6 +180,9 @@ PUBLISHED: // Deprecated methods, will become private soon void attach_character(BulletBaseCharacterControllerNode *node); void remove_character(BulletBaseCharacterControllerNode *node); + void attach_vehicle(BulletVehicle *vehicle); + void remove_vehicle(BulletVehicle *vehicle); + void remove_constraint(BulletConstraint *constraint); public: @@ -193,9 +192,29 @@ public: INLINE btBroadphaseInterface *get_broadphase() const; INLINE btDispatcher *get_dispatcher() const; + static LightMutex &get_global_lock(); + private: - void sync_p2b(PN_stdfloat dt, int num_substeps); - void sync_b2p(); + void do_sync_p2b(PN_stdfloat dt, int num_substeps); + void do_sync_b2p(); + + void do_attach_ghost(BulletGhostNode *node); + void do_remove_ghost(BulletGhostNode *node); + + void do_attach_rigid_body(BulletRigidBodyNode *node); + void do_remove_rigid_body(BulletRigidBodyNode *node); + + void do_attach_soft_body(BulletSoftBodyNode *node); + void do_remove_soft_body(BulletSoftBodyNode *node); + + void do_attach_character(BulletBaseCharacterControllerNode *node); + void do_remove_character(BulletBaseCharacterControllerNode *node); + + void do_attach_vehicle(BulletVehicle *vehicle); + void do_remove_vehicle(BulletVehicle *vehicle); + + void do_attach_constraint(BulletConstraint *constraint, bool linked_collision=false); + void do_remove_constraint(BulletConstraint *constraint); static void tick_callback(btDynamicsWorld *world, btScalar timestep); From 6c7894f68dab1d8096bc98bc208e051550f1ec4e Mon Sep 17 00:00:00 2001 From: deflected Date: Wed, 21 Feb 2018 15:26:38 +0100 Subject: [PATCH 089/118] terrain: set ShaderTerrainMesh heightfield wrap mode to clamp --- panda/src/grutil/shaderTerrainMesh.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/panda/src/grutil/shaderTerrainMesh.cxx b/panda/src/grutil/shaderTerrainMesh.cxx index a7f6b1d8af..8aac5f0e9a 100644 --- a/panda/src/grutil/shaderTerrainMesh.cxx +++ b/panda/src/grutil/shaderTerrainMesh.cxx @@ -150,6 +150,8 @@ void ShaderTerrainMesh::do_extract_heightfield() { } _heightfield_tex->set_minfilter(SamplerState::FT_linear); _heightfield_tex->set_magfilter(SamplerState::FT_linear); + _heightfield_tex->set_wrap_u(SamplerState::WM_clamp); + _heightfield_tex->set_wrap_v(SamplerState::WM_clamp); } /** From e0569815b536018369a39a7b3655546b4f897dc0 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 21 Feb 2018 15:56:45 +0100 Subject: [PATCH 090/118] tests: add test for prc page and one for light color temperature --- tests/pgraph/test_light.py | 20 ++++++++++++++++++++ tests/prc/test_config_page.py | 12 ++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 tests/pgraph/test_light.py create mode 100644 tests/prc/test_config_page.py diff --git a/tests/pgraph/test_light.py b/tests/pgraph/test_light.py new file mode 100644 index 0000000000..f303080b33 --- /dev/null +++ b/tests/pgraph/test_light.py @@ -0,0 +1,20 @@ +from panda3d import core + +def luminance(col): + return 0.2126 * col[0] + 0.7152 * col[1] + 0.0722 * col[2] + + +def test_light_colortemp(): + # Default is all white, assuming a D65 white point. + light = core.PointLight("light") + assert light.color == (1, 1, 1, 1) + assert light.color_temperature == 6500 + + # When setting color temp, it should preserve luminance. + for temp in range(2000, 15000): + light.color_temperature = temp + assert abs(luminance(light.color) - 1.0) < 0.001 + + # Setting it to the white point will make a white color. + light.color_temperature = 6500 + assert light.color.almost_equal((1, 1, 1, 1), 0.001) diff --git a/tests/prc/test_config_page.py b/tests/prc/test_config_page.py new file mode 100644 index 0000000000..5ca6cdb6ef --- /dev/null +++ b/tests/prc/test_config_page.py @@ -0,0 +1,12 @@ +from panda3d import core + +def test_load_unload_page(): + var = core.ConfigVariableInt("test-var", 1) + assert var.value == 1 + + page = core.load_prc_file_data("test_load_unload_page", "test-var 2") + assert page + assert var.value == 2 + + assert core.unload_prc_file(page) + assert var.value == 1 From 47a9aa4a80ec386909f3557d3dc3d2ccec0661f5 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Thu, 22 Feb 2018 02:02:02 -0700 Subject: [PATCH 091/118] bam: Simplify the resolve_*_pointers loops in BamReader This should also be a slight performance boost since breaking out of the loop upon discovering an incomplete child object means we don't bother resolving everything else just to discard it all. --- panda/src/putil/bamReader.cxx | 112 ++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 54 deletions(-) diff --git a/panda/src/putil/bamReader.cxx b/panda/src/putil/bamReader.cxx index 8831a1ca7e..d432d7a4ae 100644 --- a/panda/src/putil/bamReader.cxx +++ b/panda/src/putil/bamReader.cxx @@ -1345,35 +1345,39 @@ resolve_object_pointers(TypedWritable *object, if (child_id == 0) { // A NULL pointer is a NULL pointer. references.push_back((TypedWritable *)NULL); - - } else { - // See if we have the pointer available now. - CreatedObjs::const_iterator oi = _created_objs.find(child_id); - if (oi == _created_objs.end()) { - // No, too bad. - is_complete = false; - - } else { - const CreatedObj &child_obj = (*oi).second; - if (!child_obj._created) { - // The child object hasn't yet been created. - is_complete = false; - } else if (child_obj._change_this != NULL || child_obj._change_this_ref != NULL) { - // It's been created, but the pointer might still change. - is_complete = false; - } else { - if (require_fully_complete && - _object_pointers.find(child_id) != _object_pointers.end()) { - // It's not yet complete itself. - is_complete = false; - - } else { - // Yes, it's ready. - references.push_back(child_obj._ptr); - } - } - } + continue; } + + // See if we have the pointer available now. + CreatedObjs::const_iterator oi = _created_objs.find(child_id); + if (oi == _created_objs.end()) { + // No, too bad. + is_complete = false; + break; + } + + const CreatedObj &child_obj = (*oi).second; + if (!child_obj._created) { + // The child object hasn't yet been created. + is_complete = false; + break; + } + + if (child_obj._change_this != NULL || child_obj._change_this_ref != NULL) { + // It's been created, but the pointer might still change. + is_complete = false; + break; + } + + if (require_fully_complete && + _object_pointers.find(child_id) != _object_pointers.end()) { + // It's not yet complete itself. + is_complete = false; + break; + } + + // Yes, it's ready. + references.push_back(child_obj._ptr); } if (is_complete) { @@ -1433,33 +1437,33 @@ resolve_cycler_pointers(PipelineCyclerBase *cycler, if (child_id == 0) { // A NULL pointer is a NULL pointer. references.push_back((TypedWritable *)NULL); - - } else { - // See if we have the pointer available now. - CreatedObjs::const_iterator oi = _created_objs.find(child_id); - if (oi == _created_objs.end()) { - // No, too bad. - is_complete = false; - - } else { - const CreatedObj &child_obj = (*oi).second; - if (child_obj._change_this != NULL || child_obj._change_this_ref != NULL) { - // It's been created, but the pointer might still change. - is_complete = false; - - } else { - if (require_fully_complete && - _object_pointers.find(child_id) != _object_pointers.end()) { - // It's not yet complete itself. - is_complete = false; - - } else { - // Yes, it's ready. - references.push_back(child_obj._ptr); - } - } - } + continue; } + + // See if we have the pointer available now. + CreatedObjs::const_iterator oi = _created_objs.find(child_id); + if (oi == _created_objs.end()) { + // No, too bad. + is_complete = false; + break; + } + + const CreatedObj &child_obj = (*oi).second; + if (child_obj._change_this != NULL || child_obj._change_this_ref != NULL) { + // It's been created, but the pointer might still change. + is_complete = false; + break; + } + + if (require_fully_complete && + _object_pointers.find(child_id) != _object_pointers.end()) { + // It's not yet complete itself. + is_complete = false; + break; + } + + // Yes, it's ready. + references.push_back(child_obj._ptr); } if (is_complete) { From 293465a5161b621ec3dddb84e94924bcb74e0e60 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Thu, 22 Feb 2018 03:13:06 -0700 Subject: [PATCH 092/118] bam: Add sanity-check against object IDs appearing twice Found this by fuzzing; not concerned about updating the writer as the writer itself should never do this. This is just to protect against segfaults in the face of corrupt or malicious bams. --- panda/src/putil/bamReader.cxx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/panda/src/putil/bamReader.cxx b/panda/src/putil/bamReader.cxx index d432d7a4ae..30ad07415a 100644 --- a/panda/src/putil/bamReader.cxx +++ b/panda/src/putil/bamReader.cxx @@ -1159,6 +1159,16 @@ p_read_object() { // This object had already existed; thus, we are just receiving an // update for it. + if (_object_pointers.find(object_id) != _object_pointers.end()) { + // Aieee! This object isn't even complete from the last time we + // encountered it in the stream! This should never happen. Something's + // corrupt or the stream was maliciously crafted. + bam_cat.error() + << "Found object " << object_id << " in bam stream again while " + << "trying to resolve its own pointers.\n"; + return 0; + } + // Update _now_creating during this call so if this function calls // read_pointer() or register_change_this() we'll match it up properly. // This might recursively call back into this p_read_object(), so be From f8e321d155450615f1e7be41241e6ba87b687a05 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Thu, 22 Feb 2018 04:57:19 -0700 Subject: [PATCH 093/118] bam: Start more strongly checking pointer types in complete_pointers This just starts with PandaNode, and uses DCAST_INTO_R instead of DCAST to catch bad bams. BamWriter should never produce bam output that will trigger this; I found this with a fuzzer. I'm unsure about the tradeoff between bam loading performance and robustness in the face of bad bams. It certainly makes a lot of sense in debug builds, but we might want to consider a compile flag that forces bam-related asserts always on even in release builds. --- panda/src/pgraph/pandaNode.cxx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index 0d64cb0828..96b94dd0c2 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -3798,9 +3798,13 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) { int pi = CycleData::complete_pointers(p_list, manager); // Get the state and transform pointers. - _state = DCAST(RenderState, p_list[pi++]); - _transform = DCAST(TransformState, p_list[pi++]); - _prev_transform = _transform; + RenderState *state; + DCAST_INTO_R(state, p_list[pi++], pi); + _state = state; + + TransformState *transform; + DCAST_INTO_R(transform, p_list[pi++], pi); + _prev_transform = _transform = transform; /* * Finalize these pointers now to decrement their artificially-held reference @@ -3817,7 +3821,9 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) { // Get the effects pointer. - _effects = DCAST(RenderEffects, p_list[pi++]); + RenderEffects *effects; + DCAST_INTO_R(effects, p_list[pi++], pi); + _effects = effects; /* * Finalize these pointers now to decrement their artificially-held reference From 04cb128140e6d5d2d5189f274e300dc957b5ba04 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Thu, 22 Feb 2018 15:18:34 -0700 Subject: [PATCH 094/118] cftalk: Remove this It was an incomplete experiment for distributing rendering pipelines over a LAN of computers all working in concert. Who knows, it may return someday. Until then, it's best not to keep it around. --- makepanda/makepanda.vcproj | 12 --- panda/src/cftalk/cfChannel.I | 12 --- panda/src/cftalk/cfChannel.cxx | 62 --------------- panda/src/cftalk/cfChannel.h | 44 ----------- panda/src/cftalk/cfCommand.I | 41 ---------- panda/src/cftalk/cfCommand.cxx | 93 ---------------------- panda/src/cftalk/cfCommand.h | 98 ------------------------ panda/src/cftalk/config_cftalk.cxx | 46 ----------- panda/src/cftalk/config_cftalk.h | 36 --------- panda/src/cftalk/p3cftalk_composite1.cxx | 1 - panda/src/cftalk/p3cftalk_composite2.cxx | 1 - 11 files changed, 446 deletions(-) delete mode 100644 panda/src/cftalk/cfChannel.I delete mode 100644 panda/src/cftalk/cfChannel.cxx delete mode 100644 panda/src/cftalk/cfChannel.h delete mode 100644 panda/src/cftalk/cfCommand.I delete mode 100644 panda/src/cftalk/cfCommand.cxx delete mode 100644 panda/src/cftalk/cfCommand.h delete mode 100644 panda/src/cftalk/config_cftalk.cxx delete mode 100644 panda/src/cftalk/config_cftalk.h delete mode 100644 panda/src/cftalk/p3cftalk_composite1.cxx delete mode 100644 panda/src/cftalk/p3cftalk_composite2.cxx diff --git a/makepanda/makepanda.vcproj b/makepanda/makepanda.vcproj index a787653e1e..c21322f94a 100644 --- a/makepanda/makepanda.vcproj +++ b/makepanda/makepanda.vcproj @@ -3736,18 +3736,6 @@ - - - - - - - - - - - - diff --git a/panda/src/cftalk/cfChannel.I b/panda/src/cftalk/cfChannel.I deleted file mode 100644 index 4bb6a1c3fa..0000000000 --- a/panda/src/cftalk/cfChannel.I +++ /dev/null @@ -1,12 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file cfChannel.I - * @author drose - * @date 2009-03-26 - */ diff --git a/panda/src/cftalk/cfChannel.cxx b/panda/src/cftalk/cfChannel.cxx deleted file mode 100644 index e63ba79077..0000000000 --- a/panda/src/cftalk/cfChannel.cxx +++ /dev/null @@ -1,62 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file cfChannel.cxx - * @author drose - * @date 2009-03-26 - */ - -#include "cfChannel.h" - -/** - * The DatagramGenerator and DatagramSink should be newly created on the free - * store (via the new operator). The CFChannel will take ownership of these - * pointers, and will delete them when it destructs. - */ -CFChannel:: -CFChannel(DatagramGenerator *dggen, DatagramSink *dgsink) : - _dggen(dggen), - _dgsink(dgsink), - _reader(dggen), - _writer(dgsink) -{ - bool ok1 = _reader.init(); - bool ok2 = _writer.init(); - nassertv(ok1 && ok2); -} - -/** - * - */ -CFChannel:: -~CFChannel() { - delete _dggen; - delete _dgsink; -} - -/** - * Delivers a single command to the process at the other end of the channel. - */ -void CFChannel:: -send_command(CFCommand *command) { - bool ok = _writer.write_object(command); - nassertv(ok); -} - -/** - * Receives a single command from the process at the other end of the channel. - * If no command is ready, the thread will block until one is. Returns NULL - * when the connection has been closed. - */ -PT(CFCommand) CFChannel:: -receive_command() { - TypedWritable *obj = _reader.read_object(); - CFCommand *command; - DCAST_INTO_R(command, obj, NULL); - return command; -} diff --git a/panda/src/cftalk/cfChannel.h b/panda/src/cftalk/cfChannel.h deleted file mode 100644 index e549327efa..0000000000 --- a/panda/src/cftalk/cfChannel.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file cfChannel.h - * @author drose - * @date 2009-03-26 - */ - -#ifndef CFCHANNEL_H -#define CFCHANNEL_H - -#include "pandabase.h" -#include "referenceCount.h" -#include "bamReader.h" -#include "bamWriter.h" -#include "cfCommand.h" - -/** - * Represents an open communication channel in the connected-frame protocol. - * Commands may be sent and received on this channel. - */ -class EXPCL_CFTALK CFChannel : public ReferenceCount { -public: - CFChannel(DatagramGenerator *dggen, DatagramSink *dgsink); - ~CFChannel(); - - void send_command(CFCommand *command); - PT(CFCommand) receive_command(); - -private: - DatagramGenerator *_dggen; - DatagramSink *_dgsink; - BamReader _reader; - BamWriter _writer; -}; - -#include "cfChannel.I" - -#endif diff --git a/panda/src/cftalk/cfCommand.I b/panda/src/cftalk/cfCommand.I deleted file mode 100644 index fea651c96f..0000000000 --- a/panda/src/cftalk/cfCommand.I +++ /dev/null @@ -1,41 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file cfCommand.I - * @author drose - * @date 2009-02-19 - */ - -/** - * - */ -INLINE CFCommand:: -CFCommand() { -} - -/** - * - */ -INLINE CFDoCullCommand:: -CFDoCullCommand() { -} - -/** - * - */ -INLINE CFDoCullCommand:: -CFDoCullCommand(PandaNode *scene) : _scene(scene) { -} - -/** - * - */ -INLINE PandaNode *CFDoCullCommand:: -get_scene() const { - return _scene; -} diff --git a/panda/src/cftalk/cfCommand.cxx b/panda/src/cftalk/cfCommand.cxx deleted file mode 100644 index ca58163242..0000000000 --- a/panda/src/cftalk/cfCommand.cxx +++ /dev/null @@ -1,93 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file cfCommand.cxx - * @author drose - * @date 2009-02-19 - */ - -#include "cfCommand.h" - -TypeHandle CFCommand::_type_handle; -TypeHandle CFDoCullCommand::_type_handle; - -/** - * - */ -CFCommand:: -~CFCommand() { -} - -/** - * Tells the BamReader how to create objects of type CFDoCullCommand. - */ -void CFDoCullCommand:: -register_with_read_factory() { - BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); -} - -/** - * Writes the contents of this object to the datagram for shipping out to a - * Bam file. - */ -void CFDoCullCommand:: -write_datagram(BamWriter *manager, Datagram &dg) { - TypedWritable::write_datagram(manager, dg); - manager->write_pointer(dg, _scene); -} - -/** - * Called by the BamWriter when this object has not itself been modified - * recently, but it should check its nested objects for updates. - */ -void CFDoCullCommand:: -update_bam_nested(BamWriter *manager) { - manager->consider_update(_scene); -} - -/** - * Receives an array of pointers, one for each time manager->read_pointer() - * was called in fillin(). Returns the number of pointers processed. - */ -int CFDoCullCommand:: -complete_pointers(TypedWritable **p_list, BamReader *manager) { - int pi = TypedWritable::complete_pointers(p_list, manager); - - PandaNode *scene; - DCAST_INTO_R(scene, p_list[pi++], pi); - _scene = scene; - - return pi; -} - -/** - * This function is called by the BamReader's factory when a new object of - * type CFDoCullCommand is encountered in the Bam file. It should create the - * CFDoCullCommand and extract its information from the file. - */ -TypedWritable *CFDoCullCommand:: -make_from_bam(const FactoryParams ¶ms) { - CFDoCullCommand *node = new CFDoCullCommand; - DatagramIterator scan; - BamReader *manager; - - parse_params(params, scan, manager); - node->fillin(scan, manager); - - return node; -} - -/** - * This internal function is called by make_from_bam to read in all of the - * relevant data from the BamFile for the new CFDoCullCommand. - */ -void CFDoCullCommand:: -fillin(DatagramIterator &scan, BamReader *manager) { - TypedWritable::fillin(scan, manager); - manager->read_pointer(scan); -} diff --git a/panda/src/cftalk/cfCommand.h b/panda/src/cftalk/cfCommand.h deleted file mode 100644 index 84e7e1421c..0000000000 --- a/panda/src/cftalk/cfCommand.h +++ /dev/null @@ -1,98 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file cfCommand.h - * @author drose - * @date 2009-02-19 - */ - -#ifndef CFCOMMAND_H -#define CFCOMMAND_H - -#include "pandabase.h" - -#include "typedWritableReferenceCount.h" -#include "pandaNode.h" - -/** - * A single command in the Connected-Frame protocol. This can be sent client- - * to-server or server-to-client. - * - * This is an abstract base class. Individual commands will specialize from - * this. - */ -class EXPCL_CFTALK CFCommand : public TypedWritableReferenceCount { -protected: - CFCommand(); - -PUBLISHED: - virtual ~CFCommand(); - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - TypedWritableReferenceCount::init_type(); - register_type(_type_handle, "CFCommand", - TypedWritableReferenceCount::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; -}; - -/** - * Starts the cull process for a particular DisplayRegion. - */ -class EXPCL_CFTALK CFDoCullCommand : public CFCommand { -protected: - INLINE CFDoCullCommand(); -PUBLISHED: - INLINE CFDoCullCommand(PandaNode *scene); - - INLINE PandaNode *get_scene() const; - -private: - PT(PandaNode) _scene; - -public: - static void register_with_read_factory(); - virtual void write_datagram(BamWriter *manager, Datagram &dg); - virtual void update_bam_nested(BamWriter *manager); - virtual int complete_pointers(TypedWritable **plist, BamReader *manager); - -protected: - static TypedWritable *make_from_bam(const FactoryParams ¶ms); - void fillin(DatagramIterator &scan, BamReader *manager); - -public: - static TypeHandle get_class_type() { - return _type_handle; - } - static void init_type() { - CFCommand::init_type(); - register_type(_type_handle, "CFDoCullCommand", - CFCommand::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; -}; - -#include "cfCommand.I" - -#endif diff --git a/panda/src/cftalk/config_cftalk.cxx b/panda/src/cftalk/config_cftalk.cxx deleted file mode 100644 index 42c5054526..0000000000 --- a/panda/src/cftalk/config_cftalk.cxx +++ /dev/null @@ -1,46 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file config_cftalk.cxx - * @author drose - * @date 2009-03-26 - */ - -#include "config_cftalk.h" -#include "cfCommand.h" -#include "pandaSystem.h" - -ConfigureDef(config_cftalk); -NotifyCategoryDef(cftalk, ""); - -ConfigureFn(config_cftalk) { - init_libcftalk(); -} - -/** - * 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 - * called by the static initializers and need not be called explicitly, but - * special cases exist. - */ -void -init_libcftalk() { - static bool initialized = false; - if (initialized) { - return; - } - initialized = true; - - CFCommand::init_type(); - CFDoCullCommand::init_type(); - - CFDoCullCommand::register_with_read_factory(); - - PandaSystem *ps = PandaSystem::get_global_ptr(); - ps->add_system("cftalk"); -} diff --git a/panda/src/cftalk/config_cftalk.h b/panda/src/cftalk/config_cftalk.h deleted file mode 100644 index 707c5f182d..0000000000 --- a/panda/src/cftalk/config_cftalk.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * PANDA 3D SOFTWARE - * Copyright (c) Carnegie Mellon University. All rights reserved. - * - * All use of this software is subject to the terms of the revised BSD - * license. You should have received a copy of this license along - * with this source code in a file named "LICENSE." - * - * @file config_cftalk.h - * @author drose - * @date 2009-03-26 - */ - -#ifndef CONFIG_CFTALK_H -#define CONFIG_CFTALK_H - -#include "pandabase.h" -#include "windowProperties.h" -#include "notifyCategoryProxy.h" -#include "configVariableBool.h" -#include "configVariableString.h" -#include "configVariableList.h" -#include "configVariableInt.h" -#include "configVariableEnum.h" -#include "configVariableFilename.h" -#include "coordinateSystem.h" -#include "dconfig.h" - -#include "pvector.h" - -ConfigureDecl(config_cftalk, EXPCL_CFTALK, EXPTP_CFTALK); -NotifyCategoryDecl(cftalk, EXPCL_CFTALK, EXPTP_CFTALK); - -extern EXPCL_CFTALK void init_libcftalk(); - -#endif /* CONFIG_CFTALK_H */ diff --git a/panda/src/cftalk/p3cftalk_composite1.cxx b/panda/src/cftalk/p3cftalk_composite1.cxx deleted file mode 100644 index 49525d942d..0000000000 --- a/panda/src/cftalk/p3cftalk_composite1.cxx +++ /dev/null @@ -1 +0,0 @@ -#include "cfCommand.cxx" diff --git a/panda/src/cftalk/p3cftalk_composite2.cxx b/panda/src/cftalk/p3cftalk_composite2.cxx deleted file mode 100644 index 79c8aa9fa6..0000000000 --- a/panda/src/cftalk/p3cftalk_composite2.cxx +++ /dev/null @@ -1 +0,0 @@ -#include "cfChannel.cxx" From 10f1ffa9a7fd353166e7b339f94190e6363566ae Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Fri, 23 Feb 2018 01:27:34 -0700 Subject: [PATCH 095/118] express: Fix Datagram::modify_array() This just copies the array initialization out of append_data, so a COW/uninitialized Datagram can be initialized with modify_array() --- panda/src/express/datagram.I | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/panda/src/express/datagram.I b/panda/src/express/datagram.I index 0ad4007b26..fa6063805b 100644 --- a/panda/src/express/datagram.I +++ b/panda/src/express/datagram.I @@ -408,6 +408,17 @@ get_array() const { */ INLINE PTA_uchar Datagram:: modify_array() { + if (_data == (uchar *)NULL) { + // Create a new array. + _data = PTA_uchar::empty_array(0); + + } else if (_data.get_ref_count() != 1) { + // Copy on write. + PTA_uchar new_data = PTA_uchar::empty_array(0); + new_data.v() = _data.v(); + _data = new_data; + } + return _data; } From e3cc3eff8261c2b1413108ea2cda32282e6f3802 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Fri, 23 Feb 2018 01:29:04 -0700 Subject: [PATCH 096/118] putil: Optimize DatagramInputFile::get_datagram This new loop is better in two ways: 1) It reads straight into the Datagram's internal buffer, saving the trouble of allocating an intermediate buffer and wasting CPU time to copy out of it. 2) It's more cautious in the face of large (>4MB) lengths, which are more likely to be due to corruption than the datagram *actually* being that large. --- panda/src/putil/datagramInputFile.cxx | 39 +++++++++++++-------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/panda/src/putil/datagramInputFile.cxx b/panda/src/putil/datagramInputFile.cxx index 1ac14b58ed..f3bd8a081e 100644 --- a/panda/src/putil/datagramInputFile.cxx +++ b/panda/src/putil/datagramInputFile.cxx @@ -147,37 +147,34 @@ get_datagram(Datagram &data) { // Make sure we have a reasonable datagram size for putting into memory. nassertr(num_bytes == (size_t)num_bytes, false); - // Now, read the datagram itself. + // Now, read the datagram itself. We construct an empty datagram, use + // pad_bytes to make it big enough, and read *directly* into the datagram's + // internal buffer. Doing this saves us a copy operation. + data = Datagram(); - // If the number of bytes is large, we will need to allocate a temporary - // buffer from the heap. Otherwise, we can get away with allocating it on - // the stack, via alloca(). - if (num_bytes > 65536) { - char *buffer = (char *)PANDA_MALLOC_ARRAY(num_bytes); - nassertr(buffer != (char *)NULL, false); + streamsize bytes_read = 0; + while (bytes_read < num_bytes) { + streamsize bytes_left = num_bytes - bytes_read; - _in->read(buffer, num_bytes); - if (_in->fail() || _in->eof()) { - _error = true; - PANDA_FREE_ARRAY(buffer); - return false; - } + // Hold up a second - datagrams >4MB are pretty large by bam/network + // standards. Let's take it 4MB at a time just in case the length is + // corrupt, so we don't allocate potentially a few GBs of RAM only to + // find a truncated file. + bytes_left = min(bytes_left, (streamsize)4*1024*1024); - data = Datagram(buffer, num_bytes); - PANDA_FREE_ARRAY(buffer); + PTA_uchar buffer = data.modify_array(); + buffer.resize(buffer.size() + bytes_left); + unsigned char *ptr = &buffer.p()[bytes_read]; - } else { - char *buffer = (char *)alloca(num_bytes); - nassertr(buffer != (char *)NULL, false); - - _in->read(buffer, num_bytes); + _in->read((char *)ptr, bytes_left); if (_in->fail() || _in->eof()) { _error = true; return false; } - data = Datagram(buffer, num_bytes); + bytes_read += bytes_left; } + Thread::consider_yield(); return true; From 89be2c19af74c62b57961469c779b324c69979f1 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Fri, 23 Feb 2018 03:07:47 -0700 Subject: [PATCH 097/118] tests: Add tests for Datagram{,Iterator,InputFile,OutputFile} This also includes a test for my previous commit which changes DatagramInputFile::get_datagram(). --- tests/putil/test_datagram.py | 149 +++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 tests/putil/test_datagram.py diff --git a/tests/putil/test_datagram.py b/tests/putil/test_datagram.py new file mode 100644 index 0000000000..4a34bc88a2 --- /dev/null +++ b/tests/putil/test_datagram.py @@ -0,0 +1,149 @@ +import pytest +from panda3d import core + +# Fixtures for generating interesting datagrams (and verification functions) on +# the fly... + +@pytest.fixture(scope='module', + params=[False, True], + ids=['stdfloat_float', 'stdfloat_double']) +def datagram_small(request): + """Returns a small datagram, along with a verification function.""" + dg = core.Datagram() + + dg.set_stdfloat_double(request.param) + + dg.add_uint8(3) + dg.add_uint16(14159) + dg.add_uint32(0xDEADBEEF) + dg.add_uint64(0x0123456789ABCDEF) + + dg.add_int8(-77) + dg.add_int16(-1) + dg.add_int32(-972965890) + dg.add_int64(-1001001001001001) + + dg.add_string('this is a string') + dg.add_string32('this is another string') + dg.add_string('this is yet a third string') + + dg.add_stdfloat(800.2) + dg.add_stdfloat(3.1415926) + dg.add_stdfloat(2.7182818) + + def readback_function(dgi): + assert dgi.get_remaining_size() > 0 + + assert dgi.get_uint8() == 3 + assert dgi.get_uint16() == 14159 + assert dgi.get_uint32() == 0xDEADBEEF + assert dgi.get_uint64() == 0x0123456789ABCDEF + + assert dgi.get_int8() == -77 + assert dgi.get_int16() == -1 + assert dgi.get_int32() == -972965890 + assert dgi.get_int64() == -1001001001001001 + + assert dgi.get_string() == 'this is a string' + assert dgi.get_string32() == 'this is another string' + assert dgi.get_string() == 'this is yet a third string' + + assert dgi.get_stdfloat() == pytest.approx(800.2) + assert dgi.get_stdfloat() == pytest.approx(3.1415926) + assert dgi.get_stdfloat() == pytest.approx(2.7182818) + + assert dgi.get_remaining_size() == 0 + + return dg, readback_function + +@pytest.fixture(scope='module') +def datagram_large(): + """Returns a big datagram, along with a verification function.""" + + dg = core.Datagram() + for x in range(2000000): + dg.add_uint32(x) + dg.add_string('the magic words are squeamish ossifrage') + + def readback_function(dgi): + assert dgi.get_remaining_size() > 0 + + for x in range(2000000): + assert dgi.get_uint32() == x + assert dgi.get_string() == 'the magic words are squeamish ossifrage' + + assert dgi.get_remaining_size() == 0 + + return dg, readback_function + +def test_iterator(datagram_small): + """This tests Datagram/DatagramIterator, and sort of serves as a self-check + of the test fixtures too.""" + dg, verify = datagram_small + + dgi = core.DatagramIterator(dg) + verify(dgi) + + +# These test DatagramInputFile/DatagramOutputFile: + +def do_file_test(dg, verify, filename): + dof = core.DatagramOutputFile() + dof.open(filename) + dof.put_datagram(dg) + dof.close() + + dg2 = core.Datagram() + dif = core.DatagramInputFile() + dif.open(filename) + assert dif.get_datagram(dg2) + dif.close() + + # This is normally saved by the DatagramOutputFile header. We cheat here. + dg2.set_stdfloat_double(dg.get_stdfloat_double()) + + dgi = core.DatagramIterator(dg2) + verify(dgi) + +def test_file_small(datagram_small, tmpdir): + """This tests DatagramOutputFile/DatagramInputFile on small datagrams.""" + dg, verify = datagram_small + + p = tmpdir.join('datagram.bin') + filename = core.Filename.from_os_specific(str(p)) + + do_file_test(dg, verify, filename) + +def test_file_large(datagram_large, tmpdir): + """This tests DatagramOutputFile/DatagramInputFile on very large datagrams.""" + dg, verify = datagram_large + + p = tmpdir.join('datagram.bin') + filename = core.Filename.from_os_specific(str(p)) + + do_file_test(dg, verify, filename) + +def test_file_corrupt(datagram_small, tmpdir): + """This tests DatagramInputFile's handling of a corrupt size header.""" + dg, verify = datagram_small + + p = tmpdir.join('datagram.bin') + filename = core.Filename.from_os_specific(str(p)) + + dof = core.DatagramOutputFile() + dof.open(filename) + dof.put_datagram(dg) + dof.close() + + # Corrupt the size header to 4GB + with p.open(mode='wb') as f: + f.seek(0) + f.write(b'\xFF\xFF\xFF\xFF') + + dg2 = core.Datagram() + dif = core.DatagramInputFile() + dif.open(filename) + assert not dif.get_datagram(dg2) + dif.close() + + # Should we test that dg2 is unmodified? From 8fd4584bd0625b653c60465161ce15bcd57be0b4 Mon Sep 17 00:00:00 2001 From: deflected Date: Fri, 23 Feb 2018 13:37:07 +0200 Subject: [PATCH 098/118] grutil: Add control over heightfield filtering in shaderTerrainMesh Signed-off-by: deflected --- panda/src/grutil/shaderTerrainMesh.cxx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/panda/src/grutil/shaderTerrainMesh.cxx b/panda/src/grutil/shaderTerrainMesh.cxx index 8aac5f0e9a..8c9c7bd9b6 100644 --- a/panda/src/grutil/shaderTerrainMesh.cxx +++ b/panda/src/grutil/shaderTerrainMesh.cxx @@ -52,6 +52,18 @@ ConfigVariableInt stm_max_views "with. Each camera rendering the terrain corresponds to a view. Lowering this " "value will reduce the data that has to be transferred to the GPU.")); +ConfigVariableEnum stm_heightfield_minfilter +("stm-heightfield-minfilter", SamplerState::FT_linear, + PRC_DESC("This specifies the minfilter that is applied for a heightfield texture. This " + "can be used to create heightfield that is visual correct with collision " + "geometry (for example bullet terrain mesh) by changing it to nearest")); + +ConfigVariableEnum stm_heightfield_magfilter +("stm-heightfield-magfilter", SamplerState::FT_linear, + PRC_DESC("This specifies the magfilter that is applied for a heightfield texture. This " + "can be used to create heightfield that is visual correct with collision " + "geometry (for example bullet terrain mesh) by changing it to nearest")); + PStatCollector ShaderTerrainMesh::_basic_collector("Cull:ShaderTerrainMesh:Setup"); PStatCollector ShaderTerrainMesh::_lod_collector("Cull:ShaderTerrainMesh:CollectLOD"); @@ -148,8 +160,8 @@ void ShaderTerrainMesh::do_extract_heightfield() { } else { _heightfield_tex->set_format(Texture::F_r16); } - _heightfield_tex->set_minfilter(SamplerState::FT_linear); - _heightfield_tex->set_magfilter(SamplerState::FT_linear); + _heightfield_tex->set_minfilter(stm_heightfield_minfilter); + _heightfield_tex->set_magfilter(stm_heightfield_magfilter); _heightfield_tex->set_wrap_u(SamplerState::WM_clamp); _heightfield_tex->set_wrap_v(SamplerState::WM_clamp); } From 0cf605ce7df868d7ec06d421480ef8ee0dd32094 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 23 Feb 2018 21:26:49 +0100 Subject: [PATCH 099/118] showbase: move run() and __dev__ to ShowBaseGlobal Also remove ShowBaseGlobal notify category, it doesn't really add anything --- direct/src/showbase/ShowBase.py | 8 +++----- direct/src/showbase/ShowBaseGlobal.py | 9 ++++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 580f2b6ce7..8e3c223000 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -47,10 +47,6 @@ if __debug__: from . import OnScreenDebug from . import AppRunnerGlobal -def legacyRun(): - assert builtins.base.notify.warning("run() is deprecated, use base.run() instead") - builtins.base.run() - @atexit.register def exitfunc(): if getattr(builtins, 'base', None) is not None: @@ -369,7 +365,6 @@ class ShowBase(DirectObject.DirectObject): builtins.bboard = self.bboard # Config needs to be defined before ShowBase is constructed #builtins.config = self.config - builtins.run = legacyRun builtins.ostream = Notify.out() builtins.directNotify = directNotify builtins.giveNotify = giveNotify @@ -391,7 +386,9 @@ class ShowBase(DirectObject.DirectObject): # Now add this instance to the ShowBaseGlobal module scope. from . import ShowBaseGlobal + builtins.run = ShowBaseGlobal.run ShowBaseGlobal.base = self + ShowBaseGlobal.__dev__ = self.__dev__ if self.__dev__: ShowBase.notify.debug('__dev__ == %s' % self.__dev__) @@ -515,6 +512,7 @@ class ShowBase(DirectObject.DirectObject): # Remove the built-in base reference if getattr(builtins, 'base', None) is self: + del builtins.run del builtins.base del builtins.loader del builtins.taskMgr diff --git a/direct/src/showbase/ShowBaseGlobal.py b/direct/src/showbase/ShowBaseGlobal.py index 1f1c81af1a..c18ebab5fc 100644 --- a/direct/src/showbase/ShowBaseGlobal.py +++ b/direct/src/showbase/ShowBaseGlobal.py @@ -14,6 +14,7 @@ from panda3d.core import ConfigPageManager, ConfigVariableManager from panda3d.direct import get_config_showbase config = get_config_showbase() +__dev__ = config.GetBool('want-dev', __debug__) vfs = VirtualFileSystem.getGlobalPtr() ostream = Notify.out() @@ -25,6 +26,10 @@ pandaSystem = PandaSystem.getGlobalPtr() # Set direct notify categories now that we have config directNotify.setDconfigLevels() +def run(): + assert ShowBase.notify.warning("run() is deprecated, use base.run() instead") + base.run() + def inspect(anObject): # Don't use a regular import, to prevent ModuleFinder from picking # it up as a dependency when building a .p3d package. @@ -41,6 +46,4 @@ builtins.inspect = inspect # this also appears in AIBaseGlobal if (not __debug__) and __dev__: - notify = directNotify.newCategory('ShowBaseGlobal') - notify.error("You must set 'want-dev' to false in non-debug mode.") - del notify + ShowBase.notify.error("You must set 'want-dev' to false in non-debug mode.") From e6c2d3b609036724adbdac5dc0cdf802c9a997e2 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 23 Feb 2018 22:15:34 +0100 Subject: [PATCH 100/118] showbase: allow DirectGui elements to be created before ShowBase This is done by precreating aspect2d inside ShowBaseGlobal. --- direct/src/gui/DirectGuiBase.py | 45 +++++++++++++-------------- direct/src/showbase/ShowBase.py | 12 +++++-- direct/src/showbase/ShowBaseGlobal.py | 4 +++ 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/direct/src/gui/DirectGuiBase.py b/direct/src/gui/DirectGuiBase.py index 6836f7465f..a0f153d07e 100644 --- a/direct/src/gui/DirectGuiBase.py +++ b/direct/src/gui/DirectGuiBase.py @@ -80,7 +80,8 @@ __all__ = ['DirectGuiBase', 'DirectGuiWidget'] from panda3d.core import * -from panda3d.direct import get_config_showbase +from direct.showbase import ShowBaseGlobal +from direct.showbase.ShowBase import ShowBase from . import DirectGuiGlobals as DGG from .OnscreenText import * from .OnscreenGeom import * @@ -633,7 +634,7 @@ class DirectGuiBase(DirectObject.DirectObject): """ # Need to tack on gui item specific id gEvent = event + self.guiId - if get_config_showbase().GetBool('debug-directgui-msgs', False): + if ShowBase.config.GetBool('debug-directgui-msgs', False): from direct.showbase.PythonUtil import StackTrace print(gEvent) print(StackTrace()) @@ -662,7 +663,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath): # Determine the default initial state for inactive (or # unclickable) components. If we are in edit mode, these are # actually clickable by default. - guiEdit = get_config_showbase().GetBool('direct-gui-edit', 0) + guiEdit = ShowBase.config.GetBool('direct-gui-edit', False) if guiEdit: inactiveInitState = DGG.NORMAL else: @@ -723,21 +724,24 @@ class DirectGuiWidget(DirectGuiBase, NodePath): if self['guiId']: self.guiItem.setId(self['guiId']) self.guiId = self.guiItem.getId() - if __dev__: + + if ShowBaseGlobal.__dev__: guiObjectCollector.addLevel(1) guiObjectCollector.flushLevel() # track gui items by guiId for tracking down leaks - if hasattr(base, 'guiItems'): - if self.guiId in base.guiItems: - base.notify.warning('duplicate guiId: %s (%s stomping %s)' % - (self.guiId, self, - base.guiItems[self.guiId])) - base.guiItems[self.guiId] = self - if hasattr(base, 'printGuiCreates'): - printStack() + if ShowBase.config.GetBool('track-gui-items', True): + if not hasattr(ShowBase, 'guiItems'): + ShowBase.guiItems = {} + if self.guiId in ShowBase.guiItems: + ShowBase.notify.warning('duplicate guiId: %s (%s stomping %s)' % + (self.guiId, self, + ShowBase.guiItems[self.guiId])) + ShowBase.guiItems[self.guiId] = self + # Attach button to parent and make that self - if (parent == None): - parent = aspect2d + if parent is None: + parent = ShowBaseGlobal.aspect2d + self.assign(parent.attachNewNode(self.guiItem, self['sortOrder'])) # Update pose to initial values if self['pos']: @@ -1024,17 +1028,12 @@ class DirectGuiWidget(DirectGuiBase, NodePath): def destroy(self): if hasattr(self, "frameStyle"): - if __dev__: + if ShowBaseGlobal.__dev__: guiObjectCollector.subLevel(1) guiObjectCollector.flushLevel() - if hasattr(base, 'guiItems'): - if self.guiId in base.guiItems: - del base.guiItems[self.guiId] - else: - base.notify.warning( - 'DirectGuiWidget.destroy(): ' - 'gui item %s not in base.guiItems' % - self.guiId) + if hasattr(ShowBase, 'guiItems'): + ShowBase.guiItems.pop(self.guiId, None) + # Destroy children for child in self.getChildren(): childGui = self.guiDict.get(child.getName()) diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 8e3c223000..fd25018634 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -400,7 +400,8 @@ class ShowBase(DirectObject.DirectObject): if self.__dev__ or self.config.GetBool('want-e3-hacks', False): if self.config.GetBool('track-gui-items', True): # dict of guiId to gui item, for tracking down leaks - self.guiItems = {} + if not hasattr(ShowBase, 'guiItems'): + ShowBase.guiItems = {} # optionally restore the default gui sounds from 1.7.2 and earlier if ConfigVariableBool('orig-gui-sounds', False).getValue(): @@ -520,6 +521,8 @@ class ShowBase(DirectObject.DirectObject): if ShowBaseGlobal: del ShowBaseGlobal.base + self.aspect2d.node().removeAllChildren() + # [gjeon] restore sticky key settings if self.config.GetBool('disable-sticky-keys', 0): allowAccessibilityShortcutKeys(True) @@ -1102,13 +1105,18 @@ class ShowBase(DirectObject.DirectObject): self.render2d.setMaterialOff(1) self.render2d.setTwoSided(1) + # We've already created aspect2d in ShowBaseGlobal, for the + # benefit of creating DirectGui elements before ShowBase. + from . import ShowBaseGlobal + ## The normal 2-d DisplayRegion has an aspect ratio that ## matches the window, but its coordinate system is square. ## This means anything we parent to render2d gets stretched. ## For things where that makes a difference, we set up ## aspect2d, which scales things back to the right aspect ## ratio along the X axis (Z is still from -1 to 1) - self.aspect2d = self.render2d.attachNewNode(PGTop("aspect2d")) + self.aspect2d = ShowBaseGlobal.aspect2d + self.aspect2d.reparentTo(self.render2d) aspectRatio = self.getAspectRatio() self.aspect2d.setScale(1.0 / aspectRatio, 1.0, 1.0) diff --git a/direct/src/showbase/ShowBaseGlobal.py b/direct/src/showbase/ShowBaseGlobal.py index c18ebab5fc..459d5f708f 100644 --- a/direct/src/showbase/ShowBaseGlobal.py +++ b/direct/src/showbase/ShowBaseGlobal.py @@ -11,6 +11,7 @@ from .ShowBase import ShowBase, WindowControls from direct.directnotify.DirectNotifyGlobal import directNotify, giveNotify from panda3d.core import VirtualFileSystem, Notify, ClockObject, PandaSystem from panda3d.core import ConfigPageManager, ConfigVariableManager +from panda3d.core import NodePath, PGTop from panda3d.direct import get_config_showbase config = get_config_showbase() @@ -23,6 +24,9 @@ cpMgr = ConfigPageManager.getGlobalPtr() cvMgr = ConfigVariableManager.getGlobalPtr() pandaSystem = PandaSystem.getGlobalPtr() +# This is defined here so GUI elements can be instantiated before ShowBase. +aspect2d = NodePath(PGTop("aspect2d")) + # Set direct notify categories now that we have config directNotify.setDconfigLevels() From aa90b7b0c052cc8b067d4ad960664b8b40dd8716 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 23 Feb 2018 22:25:24 +0100 Subject: [PATCH 101/118] showbase: disable track-gui-items by default, remove want-e3-hacks --- direct/src/gui/DirectGuiBase.py | 2 +- direct/src/showbase/ShowBase.py | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/direct/src/gui/DirectGuiBase.py b/direct/src/gui/DirectGuiBase.py index a0f153d07e..3180d0dfce 100644 --- a/direct/src/gui/DirectGuiBase.py +++ b/direct/src/gui/DirectGuiBase.py @@ -729,7 +729,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath): guiObjectCollector.addLevel(1) guiObjectCollector.flushLevel() # track gui items by guiId for tracking down leaks - if ShowBase.config.GetBool('track-gui-items', True): + if ShowBase.config.GetBool('track-gui-items', False): if not hasattr(ShowBase, 'guiItems'): ShowBase.guiItems = {} if self.guiId in ShowBase.guiItems: diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index fd25018634..42ee315909 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -397,11 +397,10 @@ class ShowBase(DirectObject.DirectObject): self.createBaseAudioManagers() - if self.__dev__ or self.config.GetBool('want-e3-hacks', False): - if self.config.GetBool('track-gui-items', True): - # dict of guiId to gui item, for tracking down leaks - if not hasattr(ShowBase, 'guiItems'): - ShowBase.guiItems = {} + if self.__dev__ and self.config.GetBool('track-gui-items', False): + # dict of guiId to gui item, for tracking down leaks + if not hasattr(ShowBase, 'guiItems'): + ShowBase.guiItems = {} # optionally restore the default gui sounds from 1.7.2 and earlier if ConfigVariableBool('orig-gui-sounds', False).getValue(): From aaeb925e845b6a6d25d6c937c104676028e5ae05 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Fri, 23 Feb 2018 15:50:41 -0700 Subject: [PATCH 102/118] directtools: Fix typo in DirectSelection --- direct/src/directtools/DirectSelection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/direct/src/directtools/DirectSelection.py b/direct/src/directtools/DirectSelection.py index ef14fb429b..49410656b3 100644 --- a/direct/src/directtools/DirectSelection.py +++ b/direct/src/directtools/DirectSelection.py @@ -623,8 +623,8 @@ class SelectionRay(SelectionQueue): def pickBitMask(self, bitMask = BitMask32.allOff(), targetNodePath = None, skipFlags = SKIP_ALL): - if parentNodePath is None: - parentNodePath = render + if targetNodePath is None: + targetNodePath = render self.collideWithBitMask(bitMask) self.pick(targetNodePath) # Determine collision entry From 97368ec321cbe18fcd01c997df6228512842f9cf Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Fri, 23 Feb 2018 15:41:03 -0700 Subject: [PATCH 103/118] distributed: Make a few slight cleanliness changes I brought all of these over from the Astron fork --- direct/src/distributed/CRDataCache.py | 2 ++ direct/src/distributed/ClientRepositoryBase.py | 2 +- direct/src/distributed/ConnectionRepository.py | 1 + direct/src/distributed/DistributedNode.py | 2 ++ direct/src/distributed/DistributedNodeUD.py | 1 - direct/src/distributed/DistributedObjectBase.py | 9 ++++++++- 6 files changed, 14 insertions(+), 3 deletions(-) diff --git a/direct/src/distributed/CRDataCache.py b/direct/src/distributed/CRDataCache.py index 85036544de..b28453c0a3 100755 --- a/direct/src/distributed/CRDataCache.py +++ b/direct/src/distributed/CRDataCache.py @@ -2,6 +2,8 @@ from direct.distributed.CachedDOData import CachedDOData from panda3d.core import ConfigVariableInt +__all__ = ["CRDataCache"] + class CRDataCache: # Stores cached data for DistributedObjects between instantiations on the client diff --git a/direct/src/distributed/ClientRepositoryBase.py b/direct/src/distributed/ClientRepositoryBase.py index 826d3058fd..c4555e94ec 100644 --- a/direct/src/distributed/ClientRepositoryBase.py +++ b/direct/src/distributed/ClientRepositoryBase.py @@ -263,7 +263,7 @@ class ClientRepositoryBase(ConnectionRepository): distObj.setLocation(parentId, zoneId) distObj.updateRequiredFields(dclass, di) # updateRequiredFields calls announceGenerate - print("New DO:%s, dclass:%s"%(doId, dclass.getName())) + self.notify.debug("New DO:%s, dclass:%s" % (doId, dclass.getName())) return distObj def generateWithRequiredOtherFields(self, dclass, doId, di, diff --git a/direct/src/distributed/ConnectionRepository.py b/direct/src/distributed/ConnectionRepository.py index 105b77a9a9..4d6dec5b81 100644 --- a/direct/src/distributed/ConnectionRepository.py +++ b/direct/src/distributed/ConnectionRepository.py @@ -10,6 +10,7 @@ from .PyDatagramIterator import PyDatagramIterator import types import gc +__all__ = ["ConnectionRepository", "GCTrigger"] class ConnectionRepository( DoInterestManager, DoCollectionManager, CConnectionRepository): diff --git a/direct/src/distributed/DistributedNode.py b/direct/src/distributed/DistributedNode.py index 1b0c3412dc..1a88fcc02a 100644 --- a/direct/src/distributed/DistributedNode.py +++ b/direct/src/distributed/DistributedNode.py @@ -15,6 +15,8 @@ class DistributedNode(DistributedObject.DistributedObject, NodePath): self.DistributedNode_initialized = 1 self.gotStringParentToken = 0 DistributedObject.DistributedObject.__init__(self, cr) + if not self.this: + NodePath.__init__(self, "DistributedNode") # initialize gridParent self.gridParent = None diff --git a/direct/src/distributed/DistributedNodeUD.py b/direct/src/distributed/DistributedNodeUD.py index 21d6a2dbbb..39c0489f5e 100755 --- a/direct/src/distributed/DistributedNodeUD.py +++ b/direct/src/distributed/DistributedNodeUD.py @@ -1,4 +1,3 @@ -#from otp.ai.AIBaseGlobal import * from .DistributedObjectUD import DistributedObjectUD class DistributedNodeUD(DistributedObjectUD): diff --git a/direct/src/distributed/DistributedObjectBase.py b/direct/src/distributed/DistributedObjectBase.py index af5f2678f3..3d9b525067 100755 --- a/direct/src/distributed/DistributedObjectBase.py +++ b/direct/src/distributed/DistributedObjectBase.py @@ -1,4 +1,3 @@ - from direct.showbase.DirectObject import DirectObject from direct.directnotify.DirectNotifyGlobal import directNotify @@ -93,3 +92,11 @@ class DistributedObjectBase(DirectObject): def hasParentingRules(self): return self.dclass.getFieldByName('setParentingRules') != None + + def delete(self): + """ + Override this to handle cleanup right before this object + gets deleted. + """ + + pass From 167c6dcafab055a74bbec42bdf9749812e7d0ccb Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Fri, 23 Feb 2018 16:11:49 -0700 Subject: [PATCH 104/118] distributed: Change the message numbers to match Astron's I imagine very few Panda3D users depend on the message numbers having particular values. The ones removed belonged to Disney's OTP system, which hasn't been used by anybody in over 4 years. At any rate, old code should continue to work, just at the cost of compatibility between clients and servers running different P3D versions. --- direct/src/dcparser/dcClass.cxx | 8 +- direct/src/dcparser/dcField.cxx | 4 +- direct/src/dcparser/dcmsgtypes.h | 15 +- .../src/distributed/ClientRepositoryBase.py | 4 +- direct/src/distributed/MsgTypes.py | 216 ++++++++++-------- direct/src/distributed/MsgTypesCMU.py | 2 +- direct/src/distributed/OldClientRepository.py | 208 ----------------- .../src/distributed/cConnectionRepository.cxx | 10 +- .../cDistributedSmoothNodeBase.cxx | 4 +- 9 files changed, 148 insertions(+), 323 deletions(-) delete mode 100644 direct/src/distributed/OldClientRepository.py diff --git a/direct/src/dcparser/dcClass.cxx b/direct/src/dcparser/dcClass.cxx index 513d13fd90..302162c412 100644 --- a/direct/src/dcparser/dcClass.cxx +++ b/direct/src/dcparser/dcClass.cxx @@ -946,9 +946,9 @@ ai_format_generate(PyObject *distobj, DOID_TYPE do_id, bool has_optional_fields = (PyObject_IsTrue(optional_fields) != 0); if (has_optional_fields) { - packer.raw_pack_uint16(STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER); + packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER); } else { - packer.raw_pack_uint16(STATESERVER_OBJECT_GENERATE_WITH_REQUIRED); + packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED); } // Parent is a bit overloaded; this parent is not about inheritance, this @@ -1027,7 +1027,7 @@ ai_database_generate_context( packer.RAW_PACK_CHANNEL(database_server_id); packer.RAW_PACK_CHANNEL(from_channel_id); // packer.raw_pack_uint8('A'); - packer.raw_pack_uint16(STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT); + packer.raw_pack_uint16(STATESERVER_OBJECT_ENTER_WITH_REQUIRED_CONTEXT); packer.raw_pack_uint32(parent_id); packer.raw_pack_uint32(zone_id); packer.RAW_PACK_CHANNEL(owner_channel); @@ -1060,7 +1060,7 @@ ai_database_generate_context_old( packer.RAW_PACK_CHANNEL(database_server_id); packer.RAW_PACK_CHANNEL(from_channel_id); // packer.raw_pack_uint8('A'); - packer.raw_pack_uint16(STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT); + packer.raw_pack_uint16(STATESERVER_OBJECT_ENTER_WITH_REQUIRED_CONTEXT); packer.raw_pack_uint32(parent_id); packer.raw_pack_uint32(zone_id); packer.raw_pack_uint16(_number); // DCD class ID diff --git a/direct/src/dcparser/dcField.cxx b/direct/src/dcparser/dcField.cxx index 88b9786f51..a44412638a 100644 --- a/direct/src/dcparser/dcField.cxx +++ b/direct/src/dcparser/dcField.cxx @@ -391,7 +391,7 @@ Datagram DCField:: client_format_update(DOID_TYPE do_id, PyObject *args) const { DCPacker packer; - packer.raw_pack_uint16(CLIENT_OBJECT_UPDATE_FIELD); + packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD); packer.raw_pack_uint32(do_id); packer.raw_pack_uint16(_number); @@ -417,7 +417,7 @@ ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyOb packer.raw_pack_uint8(1); packer.RAW_PACK_CHANNEL(to_id); packer.RAW_PACK_CHANNEL(from_id); - packer.raw_pack_uint16(STATESERVER_OBJECT_UPDATE_FIELD); + packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD); packer.raw_pack_uint32(do_id); packer.raw_pack_uint16(_number); diff --git a/direct/src/dcparser/dcmsgtypes.h b/direct/src/dcparser/dcmsgtypes.h index e5bbe81614..0e83f384c0 100644 --- a/direct/src/dcparser/dcmsgtypes.h +++ b/direct/src/dcparser/dcmsgtypes.h @@ -17,16 +17,13 @@ // This file defines the server message types used within this module. It // duplicates some symbols defined in MsgTypes.py and AIMsgTypes.py. -#define CLIENT_OBJECT_UPDATE_FIELD 24 -#define CLIENT_CREATE_OBJECT_REQUIRED 34 -#define CLIENT_CREATE_OBJECT_REQUIRED_OTHER 35 +#define CLIENT_OBJECT_SET_FIELD 120 +#define CLIENT_ENTER_OBJECT_REQUIRED 142 +#define CLIENT_ENTER_OBJECT_REQUIRED_OTHER 143 -#define STATESERVER_OBJECT_GENERATE_WITH_REQUIRED 2001 -#define STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER 2003 -#define STATESERVER_OBJECT_UPDATE_FIELD 2004 -#define STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT 2050 -#define STATESERVER_OBJECT_CREATE_WITH_REQUIR_OTHER_CONTEXT 2051 -#define STATESERVER_BOUNCE_MESSAGE 2086 +#define STATESERVER_CREATE_OBJECT_WITH_REQUIRED 2000 +#define STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER 2001 +#define STATESERVER_OBJECT_SET_FIELD 2020 #define CLIENT_OBJECT_GENERATE_CMU 9002 diff --git a/direct/src/distributed/ClientRepositoryBase.py b/direct/src/distributed/ClientRepositoryBase.py index c4555e94ec..5aa7c3fa94 100644 --- a/direct/src/distributed/ClientRepositoryBase.py +++ b/direct/src/distributed/ClientRepositoryBase.py @@ -175,7 +175,7 @@ class ClientRepositoryBase(ConnectionRepository): "generate" messages when they are replayed(). """ - if msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER: + if msgType == CLIENT_ENTER_OBJECT_REQUIRED_OTHER: # It's a generate message. doId = extra if doId in self.deferredDoIds: @@ -381,7 +381,7 @@ class ClientRepositoryBase(ConnectionRepository): # The object had been deferred. Great; we don't even have # to generate it now. del self.deferredDoIds[doId] - i = self.deferredGenerates.index((CLIENT_CREATE_OBJECT_REQUIRED_OTHER, doId)) + i = self.deferredGenerates.index((CLIENT_ENTER_OBJECT_REQUIRED_OTHER, doId)) del self.deferredGenerates[i] if len(self.deferredGenerates) == 0: taskMgr.remove('deferredGenerate') diff --git a/direct/src/distributed/MsgTypes.py b/direct/src/distributed/MsgTypes.py index b5e5d260df..14ce6ba55e 100644 --- a/direct/src/distributed/MsgTypes.py +++ b/direct/src/distributed/MsgTypes.py @@ -3,104 +3,140 @@ from direct.showbase.PythonUtil import invertDictLossless MsgName2Id = { - # 2 new params: passwd, char bool 0/1 1 = new account - # 2 new return values: 129 = not found, 12 = bad passwd, - 'CLIENT_LOGIN': 1, - 'CLIENT_LOGIN_RESP': 2, - 'CLIENT_GET_AVATARS': 3, + 'CLIENT_HELLO': 1, + 'CLIENT_HELLO_RESP': 2, + + # Sent by the client when it's leaving. + 'CLIENT_DISCONNECT': 3, + # Sent by the server when it is dropping the connection deliberately. - 'CLIENT_GO_GET_LOST': 4, - 'CLIENT_GET_AVATARS_RESP': 5, - 'CLIENT_CREATE_AVATAR': 6, - 'CLIENT_CREATE_AVATAR_RESP': 7, - 'CLIENT_GET_FRIEND_LIST': 10, - 'CLIENT_GET_FRIEND_LIST_RESP': 11, - 'CLIENT_GET_AVATAR_DETAILS': 14, - 'CLIENT_GET_AVATAR_DETAILS_RESP': 15, - 'CLIENT_LOGIN_2': 16, - 'CLIENT_LOGIN_2_RESP': 17, + 'CLIENT_EJECT': 4, - 'CLIENT_OBJECT_UPDATE_FIELD': 24, - 'CLIENT_OBJECT_UPDATE_FIELD_RESP': 24, - 'CLIENT_OBJECT_DISABLE': 25, - 'CLIENT_OBJECT_DISABLE_RESP': 25, - 'CLIENT_OBJECT_DISABLE_OWNER': 26, - 'CLIENT_OBJECT_DISABLE_OWNER_RESP': 26, - 'CLIENT_OBJECT_DELETE': 27, - 'CLIENT_OBJECT_DELETE_RESP': 27, - 'CLIENT_SET_ZONE_CMU': 29, - 'CLIENT_REMOVE_ZONE': 30, - 'CLIENT_SET_AVATAR': 32, - 'CLIENT_CREATE_OBJECT_REQUIRED': 34, - 'CLIENT_CREATE_OBJECT_REQUIRED_RESP': 34, - 'CLIENT_CREATE_OBJECT_REQUIRED_OTHER': 35, - 'CLIENT_CREATE_OBJECT_REQUIRED_OTHER_RESP': 35, - 'CLIENT_CREATE_OBJECT_REQUIRED_OTHER_OWNER': 36, - 'CLIENT_CREATE_OBJECT_REQUIRED_OTHER_OWNER_RESP':36, + 'CLIENT_HEARTBEAT': 5, - 'CLIENT_REQUEST_GENERATES': 36, + 'CLIENT_OBJECT_SET_FIELD': 120, + 'CLIENT_OBJECT_SET_FIELDS': 121, + 'CLIENT_OBJECT_LEAVING': 132, + 'CLIENT_OBJECT_LEAVING_OWNER': 161, + 'CLIENT_ENTER_OBJECT_REQUIRED': 142, + 'CLIENT_ENTER_OBJECT_REQUIRED_OTHER': 143, + 'CLIENT_ENTER_OBJECT_REQUIRED_OWNER': 172, + 'CLIENT_ENTER_OBJECT_REQUIRED_OTHER_OWNER': 173, - 'CLIENT_DISCONNECT': 37, + 'CLIENT_DONE_INTEREST_RESP': 204, - 'CLIENT_GET_STATE_RESP': 47, - 'CLIENT_DONE_INTEREST_RESP': 48, - - 'CLIENT_DELETE_AVATAR': 49, - - 'CLIENT_DELETE_AVATAR_RESP': 5, - - 'CLIENT_HEARTBEAT': 52, - 'CLIENT_FRIEND_ONLINE': 53, - 'CLIENT_FRIEND_OFFLINE': 54, - 'CLIENT_REMOVE_FRIEND': 56, - - 'CLIENT_CHANGE_PASSWORD': 65, - - 'CLIENT_SET_NAME_PATTERN': 67, - 'CLIENT_SET_NAME_PATTERN_ANSWER': 68, - - 'CLIENT_SET_WISHNAME': 70, - 'CLIENT_SET_WISHNAME_RESP': 71, - 'CLIENT_SET_WISHNAME_CLEAR': 72, - 'CLIENT_SET_SECURITY': 73, - - 'CLIENT_SET_DOID_RANGE': 74, - - 'CLIENT_GET_AVATARS_RESP2': 75, - 'CLIENT_CREATE_AVATAR2': 76, - 'CLIENT_SYSTEM_MESSAGE': 78, - 'CLIENT_SET_AVTYPE': 80, - - 'CLIENT_GET_PET_DETAILS': 81, - 'CLIENT_GET_PET_DETAILS_RESP': 82, - - 'CLIENT_ADD_INTEREST': 97, - 'CLIENT_REMOVE_INTEREST': 99, - 'CLIENT_OBJECT_LOCATION': 102, - - 'CLIENT_LOGIN_3': 111, - 'CLIENT_LOGIN_3_RESP': 110, - - 'CLIENT_GET_FRIEND_LIST_EXTENDED': 115, - 'CLIENT_GET_FRIEND_LIST_EXTENDED_RESP': 116, - - 'CLIENT_SET_FIELD_SENDABLE': 120, - - 'CLIENT_SYSTEMMESSAGE_AKNOWLEDGE': 123, - 'CLIENT_CHANGE_GENERATE_ORDER': 124, - - # new toontown specific login message, adds last logged in, and if child account has parent acount - 'CLIENT_LOGIN_TOONTOWN': 125, - 'CLIENT_LOGIN_TOONTOWN_RESP': 126, + 'CLIENT_ADD_INTEREST': 200, + 'CLIENT_ADD_INTEREST_MULTIPLE': 201, + 'CLIENT_REMOVE_INTEREST': 203, + 'CLIENT_OBJECT_LOCATION': 140, + # These are sent internally inside the Astron cluster. - 'STATESERVER_OBJECT_GENERATE_WITH_REQUIRED': 2001, - 'STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER': 2003, - 'STATESERVER_OBJECT_UPDATE_FIELD': 2004, - 'STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT': 2050, - 'STATESERVER_OBJECT_CREATE_WITH_REQUIR_OTHER_CONTEXT': 2051, - 'STATESERVER_BOUNCE_MESSAGE': 2086, + # Message Director control messages: + 'CONTROL_CHANNEL': 1, + 'CONTROL_ADD_CHANNEL': 9000, + 'CONTROL_REMOVE_CHANNEL': 9001, + 'CONTROL_ADD_RANGE': 9002, + 'CONTROL_REMOVE_RANGE': 9003, + 'CONTROL_ADD_POST_REMOVE': 9010, + 'CONTROL_CLEAR_POST_REMOVES': 9011, + + # State Server control messages: + 'STATESERVER_CREATE_OBJECT_WITH_REQUIRED': 2000, + 'STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER': 2001, + 'STATESERVER_DELETE_AI_OBJECTS': 2009, + 'STATESERVER_OBJECT_GET_FIELD': 2010, + 'STATESERVER_OBJECT_GET_FIELD_RESP': 2011, + 'STATESERVER_OBJECT_GET_FIELDS': 2012, + 'STATESERVER_OBJECT_GET_FIELDS_RESP': 2013, + 'STATESERVER_OBJECT_GET_ALL': 2014, + 'STATESERVER_OBJECT_GET_ALL_RESP': 2015, + 'STATESERVER_OBJECT_SET_FIELD': 2020, + 'STATESERVER_OBJECT_SET_FIELDS': 2021, + 'STATESERVER_OBJECT_DELETE_FIELD_RAM': 2030, + 'STATESERVER_OBJECT_DELETE_FIELDS_RAM': 2031, + 'STATESERVER_OBJECT_DELETE_RAM': 2032, + 'STATESERVER_OBJECT_SET_LOCATION': 2040, + 'STATESERVER_OBJECT_CHANGING_LOCATION': 2041, + 'STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED': 2042, + 'STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED_OTHER': 2043, + 'STATESERVER_OBJECT_GET_LOCATION': 2044, + 'STATESERVER_OBJECT_GET_LOCATION_RESP': 2045, + 'STATESERVER_OBJECT_SET_AI': 2050, + 'STATESERVER_OBJECT_CHANGING_AI': 2051, + 'STATESERVER_OBJECT_ENTER_AI_WITH_REQUIRED': 2052, + 'STATESERVER_OBJECT_ENTER_AI_WITH_REQUIRED_OTHER': 2053, + 'STATESERVER_OBJECT_GET_AI': 2054, + 'STATESERVER_OBJECT_GET_AI_RESP': 2055, + 'STATESERVER_OBJECT_SET_OWNER': 2060, + 'STATESERVER_OBJECT_CHANGING_OWNER': 2061, + 'STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED': 2062, + 'STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED_OTHER': 2063, + 'STATESERVER_OBJECT_GET_OWNER': 2064, + 'STATESERVER_OBJECT_GET_OWNER_RESP': 2065, + 'STATESERVER_OBJECT_GET_ZONE_OBJECTS': 2100, + 'STATESERVER_OBJECT_GET_ZONES_OBJECTS': 2102, + 'STATESERVER_OBJECT_GET_CHILDREN': 2104, + 'STATESERVER_OBJECT_GET_ZONE_COUNT': 2110, + 'STATESERVER_OBJECT_GET_ZONE_COUNT_RESP': 2111, + 'STATESERVER_OBJECT_GET_ZONES_COUNT': 2112, + 'STATESERVER_OBJECT_GET_ZONES_COUNT_RESP': 2113, + 'STATESERVER_OBJECT_GET_CHILD_COUNT': 2114, + 'STATESERVER_OBJECT_GET_CHILD_COUNT_RESP': 2115, + 'STATESERVER_OBJECT_DELETE_ZONE': 2120, + 'STATESERVER_OBJECT_DELETE_ZONES': 2122, + 'STATESERVER_OBJECT_DELETE_CHILDREN': 2124, + # DBSS-backed-object messages: + 'DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS': 2200, + 'DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS_OTHER': 2201, + 'DBSS_OBJECT_GET_ACTIVATED': 2207, + 'DBSS_OBJECT_GET_ACTIVATED_RESP': 2208, + 'DBSS_OBJECT_DELETE_FIELD_DISK': 2230, + 'DBSS_OBJECT_DELETE_FIELDS_DISK': 2231, + 'DBSS_OBJECT_DELETE_DISK': 2232, + + # Database Server control messages: + 'DBSERVER_CREATE_OBJECT': 3000, + 'DBSERVER_CREATE_OBJECT_RESP': 3001, + 'DBSERVER_OBJECT_GET_FIELD': 3010, + 'DBSERVER_OBJECT_GET_FIELD_RESP': 3011, + 'DBSERVER_OBJECT_GET_FIELDS': 3012, + 'DBSERVER_OBJECT_GET_FIELDS_RESP': 3013, + 'DBSERVER_OBJECT_GET_ALL': 3014, + 'DBSERVER_OBJECT_GET_ALL_RESP': 3015, + 'DBSERVER_OBJECT_SET_FIELD': 3020, + 'DBSERVER_OBJECT_SET_FIELDS': 3021, + 'DBSERVER_OBJECT_SET_FIELD_IF_EQUALS': 3022, + 'DBSERVER_OBJECT_SET_FIELD_IF_EQUALS_RESP': 3023, + 'DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS': 3024, + 'DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS_RESP': 3025, + 'DBSERVER_OBJECT_SET_FIELD_IF_EMPTY': 3026, + 'DBSERVER_OBJECT_SET_FIELD_IF_EMPTY_RESP': 3027, + 'DBSERVER_OBJECT_DELETE_FIELD': 3030, + 'DBSERVER_OBJECT_DELETE_FIELDS': 3031, + 'DBSERVER_OBJECT_DELETE': 3032, + + # Client Agent control messages: + 'CLIENTAGENT_SET_STATE': 1000, + 'CLIENTAGENT_SET_CLIENT_ID': 1001, + 'CLIENTAGENT_SEND_DATAGRAM': 1002, + 'CLIENTAGENT_EJECT': 1004, + 'CLIENTAGENT_DROP': 1005, + 'CLIENTAGENT_GET_NETWORK_ADDRESS': 1006, + 'CLIENTAGENT_GET_NETWORK_ADDRESS_RESP': 1007, + 'CLIENTAGENT_DECLARE_OBJECT': 1010, + 'CLIENTAGENT_UNDECLARE_OBJECT': 1011, + 'CLIENTAGENT_ADD_SESSION_OBJECT': 1012, + 'CLIENTAGENT_REMOVE_SESSION_OBJECT': 1013, + 'CLIENTAGENT_SET_FIELDS_SENDABLE': 1014, + 'CLIENTAGENT_OPEN_CHANNEL': 1100, + 'CLIENTAGENT_CLOSE_CHANNEL': 1101, + 'CLIENTAGENT_ADD_POST_REMOVE': 1110, + 'CLIENTAGENT_CLEAR_POST_REMOVES': 1111, + 'CLIENTAGENT_ADD_INTEREST': 1200, + 'CLIENTAGENT_ADD_INTEREST_MULTIPLE': 1201, + 'CLIENTAGENT_REMOVE_INTEREST': 1203, } # create id->name table for debugging diff --git a/direct/src/distributed/MsgTypesCMU.py b/direct/src/distributed/MsgTypesCMU.py index c0d1ccd4ca..e7c7501730 100644 --- a/direct/src/distributed/MsgTypesCMU.py +++ b/direct/src/distributed/MsgTypesCMU.py @@ -19,7 +19,7 @@ MsgName2Id = { 'CLIENT_HEARTBEAT_CMU' : 9011, 'CLIENT_OBJECT_UPDATE_FIELD_TARGETED_CMU' : 9011, - 'CLIENT_OBJECT_UPDATE_FIELD' : 24, # Matches MsgTypes.CLIENT_OBJECT_UPDATE_FIELD + 'CLIENT_OBJECT_UPDATE_FIELD' : 120, # Matches MsgTypes.CLIENT_OBJECT_SET_FIELD } # create id->name table for debugging diff --git a/direct/src/distributed/OldClientRepository.py b/direct/src/distributed/OldClientRepository.py deleted file mode 100644 index daeb4af628..0000000000 --- a/direct/src/distributed/OldClientRepository.py +++ /dev/null @@ -1,208 +0,0 @@ -"""OldClientRepository module: contains the OldClientRepository class""" - -from .ClientRepositoryBase import * - -class OldClientRepository(ClientRepositoryBase): - """ - This is the open-source ClientRepository as provided by CMU. It - communicates with the ServerRepository in this same directory. - - If you are looking for the VR Studio's implementation of the - client repository, look to OTPClientRepository (elsewhere). - """ - notify = DirectNotifyGlobal.directNotify.newCategory("ClientRepository") - - def __init__(self, dcFileNames = None): - ClientRepositoryBase.__init__(self, dcFileNames = dcFileNames) - - # The DOID allocator. The CMU LAN server may choose to - # send us a block of DOIDs. If it chooses to do so, then we - # may create objects, using those DOIDs. - self.DOIDbase = 0 - self.DOIDnext = 0 - self.DOIDlast = 0 - - def handleSetDOIDrange(self, di): - self.DOIDbase = di.getUint32() - self.DOIDlast = self.DOIDbase + di.getUint32() - self.DOIDnext = self.DOIDbase - - def handleRequestGenerates(self, di): - # When new clients join the zone of an object, they need to hear - # about it, so we send out all of our information about objects in - # that particular zone. - - assert self.DOIDnext < self.DOIDlast - zone = di.getUint32() - for obj in self.doId2do.values(): - if obj.zone == zone: - id = obj.doId - if (self.isLocalId(id)): - self.send(obj.dclass.clientFormatGenerate(obj, id, zone, [])) - - def createWithRequired(self, className, zoneId = 0, optionalFields=None): - if self.DOIDnext >= self.DOIDlast: - self.notify.error( - "Cannot allocate a distributed object ID: all IDs used up.") - return None - id = self.DOIDnext - self.DOIDnext = self.DOIDnext + 1 - dclass = self.dclassesByName[className] - classDef = dclass.getClassDef() - if classDef == None: - self.notify.error("Could not create an undefined %s object." % ( - dclass.getName())) - obj = classDef(self) - obj.dclass = dclass - obj.zone = zoneId - obj.doId = id - self.doId2do[id] = obj - obj.generateInit() - obj._retrieveCachedData() - obj.generate() - obj.announceGenerate() - datagram = dclass.clientFormatGenerate(obj, id, zoneId, optionalFields) - self.send(datagram) - return obj - - def sendDisableMsg(self, doId): - datagram = PyDatagram() - datagram.addUint16(CLIENT_OBJECT_DISABLE) - datagram.addUint32(doId) - self.send(datagram) - - def sendDeleteMsg(self, doId): - datagram = PyDatagram() - datagram.addUint16(CLIENT_OBJECT_DELETE) - datagram.addUint32(doId) - self.send(datagram) - - def sendRemoveZoneMsg(self, zoneId, visibleZoneList=None): - datagram = PyDatagram() - datagram.addUint16(CLIENT_REMOVE_ZONE) - datagram.addUint32(zoneId) - - # if we have an explicit list of visible zones, add them - if visibleZoneList is not None: - vzl = list(visibleZoneList) - vzl.sort() - assert PythonUtil.uniqueElements(vzl) - for zone in vzl: - datagram.addUint32(zone) - - # send the message - self.send(datagram) - - def sendUpdateZone(self, obj, zoneId): - id = obj.doId - assert self.isLocalId(id) - self.sendDeleteMsg(id, 1) - obj.zone = zoneId - self.send(obj.dclass.clientFormatGenerate(obj, id, zoneId, [])) - - def sendSetZoneMsg(self, zoneId, visibleZoneList=None): - datagram = PyDatagram() - # Add message type - datagram.addUint16(CLIENT_SET_ZONE_CMU) - # Add zone id - datagram.addUint32(zoneId) - - # if we have an explicit list of visible zones, add them - if visibleZoneList is not None: - vzl = list(visibleZoneList) - vzl.sort() - assert PythonUtil.uniqueElements(vzl) - for zone in vzl: - datagram.addUint32(zone) - - # send the message - self.send(datagram) - - def isLocalId(self, id): - return ((id >= self.DOIDbase) and (id < self.DOIDlast)) - - def haveCreateAuthority(self): - return (self.DOIDlast > self.DOIDnext) - - def handleDatagram(self, di): - if self.notify.getDebug(): - print("ClientRepository received datagram:") - di.getDatagram().dumpHex(ostream) - - msgType = self.getMsgType() - - # These are the sort of messages we may expect from the public - # Panda server. - - if msgType == CLIENT_SET_DOID_RANGE: - self.handleSetDOIDrange(di) - elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_RESP: - self.handleGenerateWithRequired(di) - elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER_RESP: - self.handleGenerateWithRequiredOther(di) - elif msgType == CLIENT_OBJECT_UPDATE_FIELD_RESP: - self.handleUpdateField(di) - elif msgType == CLIENT_OBJECT_DELETE_RESP: - self.handleDelete(di) - elif msgType == CLIENT_OBJECT_DISABLE_RESP: - self.handleDisable(di) - elif msgType == CLIENT_REQUEST_GENERATES: - self.handleRequestGenerates(di) - else: - self.handleMessageType(msgType, di) - - # If we're processing a lot of datagrams within one frame, we - # may forget to send heartbeats. Keep them coming! - self.considerHeartbeat() - - def handleGenerateWithRequired(self, di): - # Get the class Id - classId = di.getUint16() - # Get the DO Id - doId = di.getUint32() - # Look up the dclass - dclass = self.dclassesByNumber[classId] - dclass.startGenerate() - # Create a new distributed object, and put it in the dictionary - distObj = self.generateWithRequiredFields(dclass, doId, di) - dclass.stopGenerate() - - def generateWithRequiredFields(self, dclass, doId, di): - if doId in self.doId2do: - # ...it is in our dictionary. - # Just update it. - distObj = self.doId2do[doId] - assert distObj.dclass == dclass - distObj.generate() - distObj.updateRequiredFields(dclass, di) - # updateRequiredFields calls announceGenerate - elif self.cache.contains(doId): - # ...it is in the cache. - # Pull it out of the cache: - distObj = self.cache.retrieve(doId) - assert distObj.dclass == dclass - # put it in the dictionary: - self.doId2do[doId] = distObj - # and update it. - distObj.generate() - distObj.updateRequiredFields(dclass, di) - # updateRequiredFields calls announceGenerate - else: - # ...it is not in the dictionary or the cache. - # Construct a new one - classDef = dclass.getClassDef() - if classDef == None: - self.notify.error("Could not create an undefined %s object." % ( - dclass.getName())) - distObj = classDef(self) - distObj.dclass = dclass - # Assign it an Id - distObj.doId = doId - # Put the new do in the dictionary - self.doId2do[doId] = distObj - # Update the required fields - distObj.generateInit() # Only called when constructed - distObj.generate() - distObj.updateRequiredFields(dclass, di) - # updateRequiredFields calls announceGenerate - return distObj diff --git a/direct/src/distributed/cConnectionRepository.cxx b/direct/src/distributed/cConnectionRepository.cxx index a19491d825..67d6ec1658 100644 --- a/direct/src/distributed/cConnectionRepository.cxx +++ b/direct/src/distributed/cConnectionRepository.cxx @@ -301,8 +301,8 @@ check_datagram() { switch (_msg_type) { #ifdef HAVE_PYTHON - case CLIENT_OBJECT_UPDATE_FIELD: - case STATESERVER_OBJECT_UPDATE_FIELD: + case CLIENT_OBJECT_SET_FIELD: + case STATESERVER_OBJECT_SET_FIELD: if (_handle_c_updates) { if (_has_owner_view) { if (!handle_update_field_owner()) { @@ -494,7 +494,7 @@ send_message_bundle(unsigned int channel, unsigned int sender_channel) { dg.add_int8(1); dg.add_uint64(channel); dg.add_uint64(sender_channel); - dg.add_uint16(STATESERVER_BOUNCE_MESSAGE); + //dg.add_uint16(STATESERVER_BOUNCE_MESSAGE); // add each bundled message BundledMsgVector::const_iterator bmi; for (bmi = _bundle_msgs.begin(); bmi != _bundle_msgs.end(); bmi++) { @@ -899,11 +899,11 @@ describe_message(ostream &out, const string &prefix, packer.RAW_UNPACK_CHANNEL(); // msg_sender msg_type = packer.raw_unpack_uint16(); - is_update = (msg_type == STATESERVER_OBJECT_UPDATE_FIELD); + is_update = (msg_type == STATESERVER_OBJECT_SET_FIELD); } else { msg_type = packer.raw_unpack_uint16(); - is_update = (msg_type == CLIENT_OBJECT_UPDATE_FIELD); + is_update = (msg_type == CLIENT_OBJECT_SET_FIELD); } if (!is_update) { diff --git a/direct/src/distributed/cDistributedSmoothNodeBase.cxx b/direct/src/distributed/cDistributedSmoothNodeBase.cxx index 70a50f33f5..14a665d9b9 100644 --- a/direct/src/distributed/cDistributedSmoothNodeBase.cxx +++ b/direct/src/distributed/cDistributedSmoothNodeBase.cxx @@ -278,12 +278,12 @@ begin_send_update(DCPacker &packer, const string &field_name) { packer.RAW_PACK_CHANNEL(_do_id); packer.RAW_PACK_CHANNEL(_ai_id); // packer.raw_pack_uint8('A'); - packer.raw_pack_uint16(STATESERVER_OBJECT_UPDATE_FIELD); + packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD); packer.raw_pack_uint32(_do_id); packer.raw_pack_uint16(field->get_number()); } else { - packer.raw_pack_uint16(CLIENT_OBJECT_UPDATE_FIELD); + packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD); packer.raw_pack_uint32(_do_id); packer.raw_pack_uint16(field->get_number()); } From f76b8a6ad89ef06c9684c942aff73e1b89bc4f9b Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Fri, 23 Feb 2018 16:45:50 -0700 Subject: [PATCH 105/118] distributed: Replace the OTP protocol format with Astron's Again, all this does is affect the 4-years-disused OTP system, leaving the CMU system entirely untouched. This changes the packet formatting in several of distributed's helper classes. --- direct/src/dcparser/dcClass.cxx | 87 +------------------ direct/src/dcparser/dcClass.h | 5 -- direct/src/distributed/DistributedObjectAI.py | 22 ++--- direct/src/distributed/DistributedObjectUD.py | 4 +- direct/src/distributed/DoInterestManager.py | 22 +++-- direct/src/distributed/PyDatagram.py | 13 ++- 6 files changed, 31 insertions(+), 122 deletions(-) diff --git a/direct/src/dcparser/dcClass.cxx b/direct/src/dcparser/dcClass.cxx index 302162c412..d11a116415 100644 --- a/direct/src/dcparser/dcClass.cxx +++ b/direct/src/dcparser/dcClass.cxx @@ -514,15 +514,9 @@ receive_update_broadcast_required_owner(PyObject *distobj, for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) { DCField *field = get_inherited_field(i); if (field->as_molecular_field() == (DCMolecularField *)NULL && - field->is_required()) { + field->is_required() && (field->is_ownrecv() || field->is_broadcast())) { packer.begin_unpack(field); - if (field->is_ownrecv()) { - field->receive_update(packer, distobj); - } else { - // It's not an ownrecv field; skip over it. It's difficult to filter - // this on the server, ask Roger for the reason. - packer.unpack_skip(); - } + field->receive_update(packer, distobj); if (!packer.end_unpack()) { break; } @@ -951,14 +945,12 @@ ai_format_generate(PyObject *distobj, DOID_TYPE do_id, packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED); } + packer.raw_pack_uint32(do_id); // Parent is a bit overloaded; this parent is not about inheritance, this // one is about the visibility container parent, i.e. the zone parent: - if (parent_id) { - packer.raw_pack_uint32(parent_id); - } + packer.raw_pack_uint32(parent_id); packer.raw_pack_uint32(zone_id); packer.raw_pack_uint16(_number); - packer.raw_pack_uint32(do_id); // Specify all of the required fields. int num_fields = get_num_inherited_fields(); @@ -1009,77 +1001,6 @@ ai_format_generate(PyObject *distobj, DOID_TYPE do_id, return Datagram(packer.get_data(), packer.get_length()); } #endif // HAVE_PYTHON -#ifdef HAVE_PYTHON -/** - * Generates a datagram containing the message necessary to create a new - * database distributed object from the AI. - * - * First Pass is to only include required values (with Defaults). - */ -Datagram DCClass:: -ai_database_generate_context( - unsigned int context_id, DOID_TYPE parent_id, ZONEID_TYPE zone_id, - CHANNEL_TYPE owner_channel, - CHANNEL_TYPE database_server_id, CHANNEL_TYPE from_channel_id) const -{ - DCPacker packer; - packer.raw_pack_uint8(1); - packer.RAW_PACK_CHANNEL(database_server_id); - packer.RAW_PACK_CHANNEL(from_channel_id); - // packer.raw_pack_uint8('A'); - packer.raw_pack_uint16(STATESERVER_OBJECT_ENTER_WITH_REQUIRED_CONTEXT); - packer.raw_pack_uint32(parent_id); - packer.raw_pack_uint32(zone_id); - packer.RAW_PACK_CHANNEL(owner_channel); - packer.raw_pack_uint16(_number); // DCD class ID - packer.raw_pack_uint32(context_id); - - // Specify all of the required fields. - int num_fields = get_num_inherited_fields(); - for (int i = 0; i < num_fields; ++i) { - DCField *field = get_inherited_field(i); - if (field->is_required() && field->as_molecular_field() == NULL) { - packer.begin_pack(field); - packer.pack_default_value(); - packer.end_pack(); - } - } - - return Datagram(packer.get_data(), packer.get_length()); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -Datagram DCClass:: -ai_database_generate_context_old( - unsigned int context_id, DOID_TYPE parent_id, ZONEID_TYPE zone_id, - CHANNEL_TYPE database_server_id, CHANNEL_TYPE from_channel_id) const -{ - DCPacker packer; - packer.raw_pack_uint8(1); - packer.RAW_PACK_CHANNEL(database_server_id); - packer.RAW_PACK_CHANNEL(from_channel_id); - // packer.raw_pack_uint8('A'); - packer.raw_pack_uint16(STATESERVER_OBJECT_ENTER_WITH_REQUIRED_CONTEXT); - packer.raw_pack_uint32(parent_id); - packer.raw_pack_uint32(zone_id); - packer.raw_pack_uint16(_number); // DCD class ID - packer.raw_pack_uint32(context_id); - - // Specify all of the required fields. - int num_fields = get_num_inherited_fields(); - for (int i = 0; i < num_fields; ++i) { - DCField *field = get_inherited_field(i); - if (field->is_required() && field->as_molecular_field() == NULL) { - packer.begin_pack(field); - packer.pack_default_value(); - packer.end_pack(); - } - } - - return Datagram(packer.get_data(), packer.get_length()); -} -#endif // HAVE_PYTHON /** * Write a string representation of this instance to . diff --git a/direct/src/dcparser/dcClass.h b/direct/src/dcparser/dcClass.h index 9e981a0097..73ad9e43d3 100644 --- a/direct/src/dcparser/dcClass.h +++ b/direct/src/dcparser/dcClass.h @@ -117,11 +117,6 @@ PUBLISHED: Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id, ZONEID_TYPE zone_id, PyObject *optional_fields) const; - Datagram ai_database_generate_context(unsigned int context_id, DOID_TYPE parent_id, ZONEID_TYPE zone_id, CHANNEL_TYPE owner_channel, - CHANNEL_TYPE database_server_id, CHANNEL_TYPE from_channel_id) const; - Datagram ai_database_generate_context_old(unsigned int context_id, DOID_TYPE parent_id, ZONEID_TYPE zone_id, - CHANNEL_TYPE database_server_id, CHANNEL_TYPE from_channel_id) const; - #endif public: diff --git a/direct/src/distributed/DistributedObjectAI.py b/direct/src/distributed/DistributedObjectAI.py index efe5cd02bf..847c0d7657 100644 --- a/direct/src/distributed/DistributedObjectAI.py +++ b/direct/src/distributed/DistributedObjectAI.py @@ -146,8 +146,6 @@ class DistributedObjectAI(DistributedObjectBase): barrier.cleanup() self.__barriers = {} - self.air.stopTrackRequestDeletedDO(self) - # DCR: I've re-enabled this block of code so that Toontown's # AI won't leak channels. # Let me know if it causes trouble. @@ -155,10 +153,9 @@ class DistributedObjectAI(DistributedObjectBase): ### block until a solution is thought out of how to prevent ### this delete message or to handle this message better # TODO: do we still need this check? - if not hasattr(self, "doNotDeallocateChannel"): - if self.air and not hasattr(self.air, "doNotDeallocateChannel"): - if self.air.minChannel <= self.doId <= self.air.maxChannel: - self.air.deallocateChannel(self.doId) + if not getattr(self, "doNotDeallocateChannel", False): + if self.air: + self.air.deallocateChannel(self.doId) self.air = None self.parentId = None @@ -200,9 +197,6 @@ class DistributedObjectAI(DistributedObjectBase): """ pass - def addInterest(self, zoneId, note="", event=None): - self.air.addInterest(self.doId, zoneId, note, event) - def b_setLocation(self, parentId, zoneId): self.d_setLocation(parentId, zoneId) self.setLocation(parentId, zoneId) @@ -274,9 +268,6 @@ class DistributedObjectAI(DistributedObjectBase): dclass.receiveUpdateOther(self, di) - def sendSetZone(self, zoneId): - self.air.sendSetZone(self, zoneId) - def startMessageBundle(self, name): self.air.startMessageBundle(name) def sendMessageBundle(self): @@ -349,10 +340,10 @@ class DistributedObjectAI(DistributedObjectBase): self.air.sendUpdate(self, fieldName, args) def GetPuppetConnectionChannel(self, doId): - return doId + (1 << 32) + return doId + (1001L << 32) def GetAccountConnectionChannel(self, doId): - return doId + (3 << 32) + return doId + (1003L << 32) def GetAccountIDFromChannelCode(self, channel): return channel >> 32 @@ -482,7 +473,6 @@ class DistributedObjectAI(DistributedObjectBase): (self.__class__, doId)) return self.air.requestDelete(self) - self.air.startTrackRequestDeletedDO(self) self._DOAI_requestedDelete = True def taskName(self, taskString): @@ -581,3 +571,5 @@ class DistributedObjectAI(DistributedObjectBase): """ This is a no-op on the AI. """ pass + def setAI(self, aiChannel): + self.air.setAI(self.doId, aiChannel) diff --git a/direct/src/distributed/DistributedObjectUD.py b/direct/src/distributed/DistributedObjectUD.py index c67d0004cc..e9e717a557 100755 --- a/direct/src/distributed/DistributedObjectUD.py +++ b/direct/src/distributed/DistributedObjectUD.py @@ -270,10 +270,10 @@ class DistributedObjectUD(DistributedObjectBase): self.air.sendUpdate(self, fieldName, args) def GetPuppetConnectionChannel(self, doId): - return doId + (1 << 32) + return doId + (1001L << 32) def GetAccountConnectionChannel(self, doId): - return doId + (3 << 32) + return doId + (1003L << 32) def GetAccountIDFromChannelCode(self, channel): return channel >> 32 diff --git a/direct/src/distributed/DoInterestManager.py b/direct/src/distributed/DoInterestManager.py index eb616d3f26..08a2f21657 100755 --- a/direct/src/distributed/DoInterestManager.py +++ b/direct/src/distributed/DoInterestManager.py @@ -111,7 +111,7 @@ class DoInterestManager(DirectObject.DirectObject): self._allInterestsCompleteCallbacks = [] def __verbose(self): - return self.InterestDebug or self.getVerbose() + return self.InterestDebug.getValue() or self.getVerbose() def _getAnonymousEvent(self, desc): return 'anonymous-%s-%s' % (desc, DoInterestManager._SerialGen.next()) @@ -504,18 +504,23 @@ class DoInterestManager(DirectObject.DirectObject): 'trying to set interest to invalid parent: %s' % parentId) datagram = PyDatagram() # Add message type - datagram.addUint16(CLIENT_ADD_INTEREST) - datagram.addUint16(handle) - datagram.addUint32(contextId) - datagram.addUint32(parentId) if isinstance(zoneIdList, list): vzl = list(zoneIdList) vzl.sort() uniqueElements(vzl) + datagram.addUint16(CLIENT_ADD_INTEREST_MULTIPLE) + datagram.addUint32(contextId) + datagram.addUint16(handle) + datagram.addUint32(parentId) + datagram.addUint16(len(vzl)) for zone in vzl: datagram.addUint32(zone) else: - datagram.addUint32(zoneIdList) + datagram.addUint16(CLIENT_ADD_INTEREST) + datagram.addUint32(contextId) + datagram.addUint16(handle) + datagram.addUint32(parentId) + datagram.addUint32(zoneIdList) self.send(datagram) def _sendRemoveInterest(self, handle, contextId): @@ -530,9 +535,8 @@ class DoInterestManager(DirectObject.DirectObject): datagram = PyDatagram() # Add message type datagram.addUint16(CLIENT_REMOVE_INTEREST) + datagram.addUint32(contextId) datagram.addUint16(handle) - if contextId != 0: - datagram.addUint32(contextId) self.send(datagram) if __debug__: state = DoInterestManager._interests[handle] @@ -583,8 +587,8 @@ class DoInterestManager(DirectObject.DirectObject): This handles the interest done messages and may dispatch an event """ assert DoInterestManager.notify.debugCall() - handle = di.getUint16() contextId = di.getUint32() + handle = di.getUint16() if self.__verbose(): print('CR::INTEREST.interestDone(handle=%s)' % handle) DoInterestManager.notify.debug( diff --git a/direct/src/distributed/PyDatagram.py b/direct/src/distributed/PyDatagram.py index 74e73fbf28..eebc06e440 100755 --- a/direct/src/distributed/PyDatagram.py +++ b/direct/src/distributed/PyDatagram.py @@ -7,7 +7,7 @@ from panda3d.core import Datagram from panda3d.direct import * # Import the type numbers -#from otp.ai.AIMsgTypes import * +from direct.distributed.MsgTypes import * class PyDatagram(Datagram): @@ -47,13 +47,10 @@ class PyDatagram(Datagram): self.addUint16(code) -# def addServerControlHeader(self, code): -# self.addInt8(1) -# self.addChannel(CONTROL_MESSAGE) -# self.addUint16(code) -# def addOldServerControlHeader(self, code): -# self.addChannel(CONTROL_MESSAGE) -# self.addUint16(code) + def addServerControlHeader(self, code): + self.addInt8(1) + self.addChannel(CONTROL_CHANNEL) + self.addUint16(code) def putArg(self, arg, subatomicType, divisor=1): if (divisor == 1): From b72d1c198f3bc3d302b434f53f71f4c513908261 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Fri, 23 Feb 2018 19:52:37 -0700 Subject: [PATCH 106/118] distributed: Fix the import test (and syntax on Python 3) --- direct/src/distributed/DistributedObjectAI.py | 4 ++-- direct/src/distributed/DistributedObjectUD.py | 4 ++-- makepanda/test_imports.py | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/direct/src/distributed/DistributedObjectAI.py b/direct/src/distributed/DistributedObjectAI.py index 847c0d7657..32b2374fc5 100644 --- a/direct/src/distributed/DistributedObjectAI.py +++ b/direct/src/distributed/DistributedObjectAI.py @@ -340,10 +340,10 @@ class DistributedObjectAI(DistributedObjectBase): self.air.sendUpdate(self, fieldName, args) def GetPuppetConnectionChannel(self, doId): - return doId + (1001L << 32) + return doId + (1001 << 32) def GetAccountConnectionChannel(self, doId): - return doId + (1003L << 32) + return doId + (1003 << 32) def GetAccountIDFromChannelCode(self, channel): return channel >> 32 diff --git a/direct/src/distributed/DistributedObjectUD.py b/direct/src/distributed/DistributedObjectUD.py index e9e717a557..99762cc1d3 100755 --- a/direct/src/distributed/DistributedObjectUD.py +++ b/direct/src/distributed/DistributedObjectUD.py @@ -270,10 +270,10 @@ class DistributedObjectUD(DistributedObjectBase): self.air.sendUpdate(self, fieldName, args) def GetPuppetConnectionChannel(self, doId): - return doId + (1001L << 32) + return doId + (1001 << 32) def GetAccountConnectionChannel(self, doId): - return doId + (1003L << 32) + return doId + (1003 << 32) def GetAccountIDFromChannelCode(self, channel): return channel >> 32 diff --git a/makepanda/test_imports.py b/makepanda/test_imports.py index 290ec03e13..ecd87cbb66 100644 --- a/makepanda/test_imports.py +++ b/makepanda/test_imports.py @@ -98,7 +98,6 @@ import direct.distributed.InterestWatcher import direct.distributed.MsgTypes import direct.distributed.MsgTypesCMU import direct.distributed.NetMessenger -import direct.distributed.OldClientRepository import direct.distributed.ParentMgr import direct.distributed.PyDatagram import direct.distributed.PyDatagramIterator From a5beccc99eb9224c3e632530f8acaa2e6e28d609 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 24 Feb 2018 12:15:56 +0100 Subject: [PATCH 107/118] chan: fix crash when AnimControl is destroyed in threaded pipeline See #254 for related discussion. --- panda/src/chan/animControl.cxx | 2 +- panda/src/chan/partBundle.cxx | 19 +++++++++++++++++++ panda/src/chan/partBundle.h | 2 ++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/panda/src/chan/animControl.cxx b/panda/src/chan/animControl.cxx index 09660d22c0..35e56374f7 100644 --- a/panda/src/chan/animControl.cxx +++ b/panda/src/chan/animControl.cxx @@ -103,7 +103,7 @@ fail_anim(PartBundle *part) { */ AnimControl:: ~AnimControl() { - get_part()->set_control_effect(this, 0.0f); + get_part()->control_removed(this); } /** diff --git a/panda/src/chan/partBundle.cxx b/panda/src/chan/partBundle.cxx index 3fb797e3c3..f519d09a60 100644 --- a/panda/src/chan/partBundle.cxx +++ b/panda/src/chan/partBundle.cxx @@ -539,6 +539,25 @@ control_activated(AnimControl *control) { } } +/** + * Called by the AnimControl when it destructs. This needs to remove the + * AnimControl pointer from all pipeline stages. + */ +void PartBundle:: +control_removed(AnimControl *control) { + nassertv(control->get_part() == this); + + OPEN_ITERATE_ALL_STAGES(_cycler) { + CDStageWriter cdata(_cycler, pipeline_stage); + ChannelBlend::iterator cbi = cdata->_blend.find(control); + if (cbi != cdata->_blend.end()) { + cdata->_blend.erase(cbi); + cdata->_anim_changed = true; + } + } + CLOSE_ITERATE_ALL_STAGES(_cycler); +} + /** * The internal implementation of bind_anim(), this receives a pointer to an * uninitialized AnimControl and fills it in if the bind is successful. diff --git a/panda/src/chan/partBundle.h b/panda/src/chan/partBundle.h index 60d2b66b8b..80ddb4122d 100644 --- a/panda/src/chan/partBundle.h +++ b/panda/src/chan/partBundle.h @@ -149,6 +149,7 @@ public: // The following functions aren't really part of the public interface; // they're just public so we don't have to declare a bunch of friends. virtual void control_activated(AnimControl *control); + void control_removed(AnimControl *control); INLINE void set_update_delay(double delay); bool do_bind_anim(AnimControl *control, AnimBundle *anim, @@ -204,6 +205,7 @@ private: typedef CycleDataLockedReader CDLockedReader; typedef CycleDataReader CDReader; typedef CycleDataWriter CDWriter; + typedef CycleDataStageWriter CDStageWriter; public: static void register_with_read_factory(); From c3106411f54da1e7ecdfaf3852377c485ff73ba5 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Sun, 25 Feb 2018 18:26:43 -0700 Subject: [PATCH 108/118] dtool: Remove 'attach' See #244 for discussion. It's really old, hasn't been used in almost a decade, and doesn't support the current Git workflow either. --- dtool/src/attach/ctallihave | 33 - dtool/src/attach/ctattach.drv | 150 ----- dtool/src/attach/ctattachcc | 86 --- dtool/src/attach/ctattch.pl | 530 ---------------- dtool/src/attach/ctattch.pl.rnd | 954 ---------------------------- dtool/src/attach/ctccase.pl | 430 ------------- dtool/src/attach/ctci | 70 -- dtool/src/attach/ctcm.pl | 579 ----------------- dtool/src/attach/ctco | 69 -- dtool/src/attach/ctcvs.pl | 451 ------------- dtool/src/attach/ctdelta | 57 -- dtool/src/attach/ctdelta.pl | 232 ------- dtool/src/attach/ctdelta.pl.rnd | 232 ------- dtool/src/attach/ctihave | 33 - dtool/src/attach/ctmake | 23 - dtool/src/attach/ctmkdir | 55 -- dtool/src/attach/ctmkelem | 78 --- dtool/src/attach/ctmv | 35 - dtool/src/attach/ctntool.pl | 258 -------- dtool/src/attach/ctproj.pl | 60 -- dtool/src/attach/ctquery | 42 -- dtool/src/attach/ctquery.pl | 37 -- dtool/src/attach/ctrm | 31 - dtool/src/attach/ctsanity | 43 -- dtool/src/attach/cttimewarp | 43 -- dtool/src/attach/ctunattach.drv | 49 -- dtool/src/attach/ctunattach.pl | 251 -------- dtool/src/attach/ctunco | 31 - dtool/src/attach/ctutils.pl | 47 -- dtool/src/attach/ctvspec.pl | 360 ----------- dtool/src/attach/dtool.alias | 37 -- dtool/src/attach/dtool.alias-sh | 46 -- dtool/src/attach/dtool.cshrc | 99 --- dtool/src/attach/dtool.emacs | 295 --------- dtool/src/attach/dtool.init | 10 - dtool/src/attach/dtool.sh | 81 --- dtool/src/attach/get-cttree | 104 --- dtool/src/attach/get-delta | 481 -------------- dtool/src/attach/neartool | 1056 ------------------------------- dtool/src/attach/unco.pl | 31 - dtool/src/attach/update-cttree | 189 ------ 41 files changed, 7778 deletions(-) delete mode 100755 dtool/src/attach/ctallihave delete mode 100755 dtool/src/attach/ctattach.drv delete mode 100755 dtool/src/attach/ctattachcc delete mode 100644 dtool/src/attach/ctattch.pl delete mode 100644 dtool/src/attach/ctattch.pl.rnd delete mode 100644 dtool/src/attach/ctccase.pl delete mode 100644 dtool/src/attach/ctci delete mode 100644 dtool/src/attach/ctcm.pl delete mode 100644 dtool/src/attach/ctco delete mode 100644 dtool/src/attach/ctcvs.pl delete mode 100755 dtool/src/attach/ctdelta delete mode 100644 dtool/src/attach/ctdelta.pl delete mode 100644 dtool/src/attach/ctdelta.pl.rnd delete mode 100755 dtool/src/attach/ctihave delete mode 100644 dtool/src/attach/ctmake delete mode 100644 dtool/src/attach/ctmkdir delete mode 100644 dtool/src/attach/ctmkelem delete mode 100644 dtool/src/attach/ctmv delete mode 100644 dtool/src/attach/ctntool.pl delete mode 100644 dtool/src/attach/ctproj.pl delete mode 100755 dtool/src/attach/ctquery delete mode 100644 dtool/src/attach/ctquery.pl delete mode 100644 dtool/src/attach/ctrm delete mode 100644 dtool/src/attach/ctsanity delete mode 100755 dtool/src/attach/cttimewarp delete mode 100644 dtool/src/attach/ctunattach.drv delete mode 100644 dtool/src/attach/ctunattach.pl delete mode 100644 dtool/src/attach/ctunco delete mode 100644 dtool/src/attach/ctutils.pl delete mode 100644 dtool/src/attach/ctvspec.pl delete mode 100644 dtool/src/attach/dtool.alias delete mode 100644 dtool/src/attach/dtool.alias-sh delete mode 100644 dtool/src/attach/dtool.cshrc delete mode 100644 dtool/src/attach/dtool.emacs delete mode 100644 dtool/src/attach/dtool.init delete mode 100755 dtool/src/attach/dtool.sh delete mode 100755 dtool/src/attach/get-cttree delete mode 100755 dtool/src/attach/get-delta delete mode 100755 dtool/src/attach/neartool delete mode 100644 dtool/src/attach/unco.pl delete mode 100755 dtool/src/attach/update-cttree diff --git a/dtool/src/attach/ctallihave b/dtool/src/attach/ctallihave deleted file mode 100755 index 5e6f7f88ed..0000000000 --- a/dtool/src/attach/ctallihave +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/perl - -if ($#ARGV != -1) { - exit print "Usage: ctihave\n" ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "not configured for using CTtools\n" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$projs = $ENV{"CTPROJS"} ; -@projsplit = split( / +/, $projs ) ; - -foreach $item ( @projsplit ) { - @items = split( /:/, $item ) ; - $thisproj = $items[0] ; - $thisflav = $items[1] ; - $thisspec = &CTResolveSpec( $thisproj, $thisflav ) ; - $result = $result . &CTCMIHave( $thisproj, $thisflav, $thisspec ) ; -} -if ( $result ne "" ) { - @splitlist = split( /\n/, $result ) ; - foreach $item ( @splitlist ) { - print $item . "\n" ; - } -} diff --git a/dtool/src/attach/ctattach.drv b/dtool/src/attach/ctattach.drv deleted file mode 100755 index c5b02718b8..0000000000 --- a/dtool/src/attach/ctattach.drv +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/perl - -# acceptable forms: -# ctattach - give usage message -# ctattach project - attach to the personal flavor of the project -# ctattach project flavor - attach to a specific flavor of the project -# ctattach - - list projects that can be attached to -# ctattach project - - list flavors of a given project -# ctattach - flavor - list projects with a certain flavor -# ctattach -def project flavor - attach to project, setting CTDEFAULT_FLAV -# to flavor for the scope of this attach - -sub CTAttachUsage { - print STDERR "Usage: ctattach -def project flavor -or-\n" ; - print STDERR " ctattach project [flavor] -or-\n" ; - print STDERR " ctattach project - -or-\n" ; - print STDERR " ctattach - [flavor]\n" ; - &CTAttachWriteNullScript( $tmpname ) ; - print $tmpname . "\n" ; - exit; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "\$" . "DTOOL environment must be set to use CTtools\n" ; -} - -require "$tool/built/include/ctattch.pl" ; - -$tmpname = "/tmp/script.$$" ; - -if ( $#ARGV == -1 ) { - &CTUDebug( "got no arguments\n" ) ; - &CTAttachUsage ; -} - -$idx = 0 ; -$proj = "" ; -$flav = "" ; -$noflav = 0 ; -$defflav = "" ; -$spread = 0 ; -$anydef = 0 ; - -# -# parse arguemnts -# - -if ( $ARGV[$idx] eq "-def" ) { - &CTUDebug( "got '-def' parameter\n" ) ; - if ( $#ARGV < ($idx + 2) ) { - &CTUDebug( "not enough arguments after -def\n" ) ; - &CTAttachUsage ; - } - $defflav = $ARGV[$idx+2] ; - $spread = 1; - &CTUDebug( "spread default flavor is '$defflav'\n" ) ; - $idx++ ; -} else { - if ( $ENV{"CTDEFAULT_FLAV"} ne "" ) { - $defflav = $ENV{"CTDEFAULT_FLAV"} ; - &CTUDebug( "environment default flavor is '$defflav'\n" ) ; - } -} - -$proj = $ARGV[$idx] ; -&CTUDebug( "project is '$proj'\n" ) ; - -if ( $defflav eq "" ) { - $defflav = "default" ; - &CTUDebug( "no environmental default, using 'default'\n" ) ; -} - -if ( $#ARGV > $idx ) { - $flav = $ARGV[$idx+1] ; - &CTUDebug( "provided flavor is '$flav'\n" ) ; -} else { - if ( $proj ne "-" ) { - $flav = $defflav; - &CTUDebug( "using environment default flavor '$flav'\n" ) ; - $noflav = 1 ; - } -} - -if (( $noflav == 1 ) || ( $flav eq "default" )) { - $anydef = 1 ; -} - -# -# act on the arguments we got -# - -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctvspec.pl" ; - -if (( $proj eq "-" ) || ( $flav eq "-" )) { - if ( $#ARGV == 0 ) { - # list projects that can be attached to - print STDERR "Projects that can be attached to:\n" ; - $_ = &CTListAllProjects ; - @projlist = split ; - foreach $item ( @projlist ) { - print STDERR " $item\n" ; - } - } elsif ( $proj eq "-" ) { - # list project that have a given flavor - print STDERR "Projects that have a '$flav' flavor:\n" ; - $_ = &CTListAllProjects ; - @projlist = split ; - foreach $item ( @projlist ) { - $tmp = &CTResolveSpec( $item, $flav ) ; - if ( $tmp ne "" ) { - print STDERR " $item\n" ; - } - } - } else { - # list flavors of a given project - print STDERR "Flavors of project '$proj':\n" ; - $_ = &CTListAllFlavors( $proj ) ; - @flavlist = split ; - foreach $item ( @flavlist ) { - print STDERR " $item\n" ; - } - } - &CTAttachWriteNullScript( $tmpname ) ; - print $tmpname . "\n" ; -} else { - # output a real attachment - $curflav = &CTQueryProj( $proj ) ; - if (( $curflav eq "" ) || ( $noflav == 0 )) { - $envsep{"PATH"} = ":" ; - $envsep{"LD_LIBRARY_PATH"} = ":" ; - $envsep{"DYLD_LIBRARY_PATH"} = ":" ; - $envsep{"PFPATH"} = ":" ; - $envsep{"SSPATH"} = ":" ; - $envsep{"STKPATH"} = ":" ; - $envsep{"DC_PATH"} = ":" ; - $spec = &CTAttachCompute( $proj, $flav, $anydef ) ; - if ( $spec eq "" ) { - &CTAttachWriteNullScript( $tmpname ) ; - print $tmpname . "\n" ; - } else { - &CTAttachWriteScript( $tmpname ) ; - print $tmpname . "\n" ; - } - } else { - &CTAttachWriteNullScript( $tmpname ) ; - print $tmpname . "\n" ; - } -} diff --git a/dtool/src/attach/ctattachcc b/dtool/src/attach/ctattachcc deleted file mode 100755 index 0a9993c453..0000000000 --- a/dtool/src/attach/ctattachcc +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/perl - -if ( $#ARGV != 5 ) { - print STDERR "This is for internal use by attach ONLY\n" ; - exit ; -} - -$root = $ARGV[0] ; -$view = $ARGV[1] ; -$branch = $ARGV[2] ; -$label = $ARGV[3] ; -$vobname = $ARGV[4] ; -$proj = $ARGV[5] ; -$tmpname = "/tmp/config.$$" ; - -$emitted = 0 ; - -$ctdebug = $ENV{"CTATTACH_DEBUG"} ; - -if ($ctdebug) { - print STDERR "Params:\n 0: '$root'\n 1: '$view'\n 2: '$branch'\n" ; - print STDERR " 3: '$label'\n 4: '$vobname'\n 5: '$proj'\n" ; - print STDERR "making branch and label types for view " . $view . "\n" ; - print STDERR "executing: /usr/atria/bin/cleartool mkbrtype -vob /vobs/$vobname -c \"Branch type for the $view view\" $branch 2> /dev/null > /dev/null\n" ; - print STDERR "executing: /usr/atria/bin/cleartool mklbtype -vob /vobs/$vobname -c \"Label type for the $view view\" $label 2> /dev/null > /dev/null\n" ; -} -system "/usr/atria/bin/cleartool mkbrtype -vob /vobs/$vobname -c \"Branch type for the $view view\" $branch 2> /dev/null > /dev/null\n" ; -system "/usr/atria/bin/cleartool mklbtype -vob /vobs/$vobname -c \"Label type for the $view view\" $label 2> /dev/null > /dev/null\n" ; - -if ($ctdebug) { - print STDERR "creating/updating the config-spec for view " . $view . "\n" ; -} -open( CTINTERFACE, "/usr/atria/bin/cleartool catcs -tag $view |" ) ; -open( TMPFILE, "> $tmpname" ) ; -while ( ) { - if ( $_ =~ "CHECKEDOUT" ) { - if ($ctdebug) { - print STDERR "case 1:\noutputting: '$_'\n" ; - } - print TMPFILE "$_" ; - } elsif (( $_ =~ /^element \*/ ) && ( $_ =~ "/main/LATEST" ) && - !( $_ =~ /\/$proj\// )) { - if ( ! $emitted ) { - $emitted = 1 ; - print TMPFILE "element $root/... .../$branch/LATEST\n" ; - print TMPFILE "element $root/... $label -mkbranch $branch\n" ; - print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; - if ($ctdebug) { - print STDERR "case 2:\n" ; - print STDERR "outputting: 'element $root/... .../$branch/LATEST'\n" ; - print STDERR "outputting: 'element $root/... $label -mkbranch $branch'\n" ; - print STDERR "outputting: 'element $root/... /main/LATEST -mkbranch $branch'\n" ; - } - } - if ($ctdebug) { - print STDERR "case 3:\n" ; - print STDERR "outputting: '$_'\n" ; - } - print TMPFILE "$_" ; - } elsif ( $_ =~ /\/$proj\// ) { - if ( ! $emitted ) { - $emitted = 1 ; - print TMPFILE "element $root/... .../$branch/LATEST\n" ; - print TMPFILE "element $root/... $label -mkbranch $branch\n" ; - print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; - if ($ctdebug) { - print STDERR "case 4:\n" ; - print STDERR "outputting: 'element $root/... .../$branch/LATEST'\n" ; - print STDERR "outputting: 'element $root/... $label -mkbranch $branch'\n" ; - print STDERR "outputting: 'element $root/... /main/LATEST -mkbranch $branch'\n" ; - } - } - } else { - if ($ctdebug) { - print STDERR "case 5:\n" ; - print STDERR "outputting: '$_'\n" ; - } - print TMPFILE "$_" ; - } -} -close( CTINTERFACE ) ; -close( TMPFILE ) ; -if ($ctdebug) { - print STDERR "output to execute: '/usr/atria/bin/cleartool setcs -tag $view $tmpname ; rm $tmpname'\n" ; -} -system "/usr/atria/bin/cleartool setcs -tag $view $tmpname ; rm $tmpname\n" ; diff --git a/dtool/src/attach/ctattch.pl b/dtool/src/attach/ctattch.pl deleted file mode 100644 index 69f1a2ed3b..0000000000 --- a/dtool/src/attach/ctattch.pl +++ /dev/null @@ -1,530 +0,0 @@ -require "$tool/built/include/ctutils.pl" ; - -$shell_type = "csh" ; -if ( $ENV{"SHELL_TYPE"} ne "" ) { - if ( $ENV{"SHELL_TYPE"} eq "sh" ) { - $shell_type = "sh" ; - } -} - -$docnt = 0 ; -@attachqueue = () ; - -require "$tool/built/include/ctquery.pl" ; - -# force set a variable in the 'new' environment -# input is in: -# $_[0] = variable -# $_[1] = value -# -# output is in: -# %newenv = variable marked to be set to value -sub CTAttachSet { - if ( ( $_[0] ne "" ) && ( $_[1] ne "" ) ) { - &CTUDebug( "setting " . $_[0] . " to '" . $_[1] . "'\n" ) ; - $newenv{$_[0]} = $_[1] ; - } -} - -# get a variable from the environment and split it out to unified format -# (ie: space separated) -# input is in: -# $_[0] = variable to get -# -# output is in: -# string returned with value -sub CTSpoolEnv { - local( $ret ) = $ENV{$_[0]} ; - if ( $envsep{$_[0]} ne "" ) { - local( @splitlist ) = split( $envsep{$_[0]}, $ret ); - $ret = join( " ", @splitlist ) ; - } - $ret ; -} - -# modify a possibly existing variable to have a value in the 'new' environment -# input is in: -# $_[0] = variable -# $_[1] = value -# $_[2] = root -# $_[3] = project -# -# output is in: -# %newenv = variable adjusted to have the new value -sub CTAttachMod { - &CTUDebug( "in CTAttachMod\n" ) ; - if ( $_[0] eq "CTPROJS" ) { - # as part of the system, this one is special - &CTUDebug( "doing a mod on $CTPROJS\n" ) ; - if ( $newenv{$_[0]} eq "" ) { - $newenv{$_[0]} = $ENV{$_[0]} ; - } - local( $proj ) = $_[3] ; - $proj =~ tr/A-Z/a-z/ ; - local( $curflav ) = &CTQueryProj( $proj ) ; - if ( $curflav ne "" ) { - local( $tmp ) = $_[3] . ":" . $curflav ; - if ( $newenv{$_[0]} =~ /$tmp/ ) { - local( $hold ) = $newenv{$_[0]} ; - $hold =~ s/$tmp/$_[1]/ ; - &CTUDebug( "already attached to " . $_[3] . " changing '" . - $tmp . "' to '" . $_[1] . "' yielding '" . $hold . - "'\n" ) ; - $newenv{$_[0]} = $hold ; - } else { - &CTUDebug( "prepending '" . $_[1] . "' to CTPROJS\n" ) ; - $newenv{$_[0]} = $_[1] . " " . $newenv{$_[0]} ; - } - } else { - &CTUDebug( "writing '" . $_[1] . "' to CTPROJS\n" ) ; - if ( $newenv{$_[0]} eq "" ) { - $newenv{$_[0]} = $_[1] ; - } else { - $newenv{$_[0]} = $_[1] . " " . $newenv{$_[0]} ; - } - } - } elsif ( ( $_[0] ne "" ) && ( $_[1] ne "" ) ) { - local( $dosimple ) = 0 ; - if ( $newenv{$_[0]} eq "" ) { - # not in our 'new' environment yet, add it. - # may still be empty - $newenv{$_[0]} = &CTSpoolEnv( $_[0] ) ; - } - if ( ! ( $newenv{$_[0]} =~ /$_[1]/ )) { - &CTUDebug( "'" . $_[1] . "' exists in " . $_[0] . - " testing for simple modification\n" ) ; - # if it's in there already, we're done before we started. - if ( $_[1] =~ /^$_[2]/ ) { - &CTUDebug( "new value contains root '" . $_[2] . - "', may not be able to do simple edit\n" ) ; - # damn, might need to do an in-place edit - local( $curroot ) = $ENV{$_[3]} ; - &CTUDebug( "current root for '" . $_[3] . "' is '" . - $curroot . "'\n" ) ; - if ( $curroot eq "" ) { - &CTUDebug( "can do simple edit\n" ) ; - $dosimple = 1 ; - } else { - local( $test ) = $_[1] ; - $test =~ s/^$_[2]// ; - $test = $curroot . $test ; - if ( $newenv{$_[0]} =~ /$test/ ) { - # there it is. in-place edit - local( $foo ) = $newenv{$_[0]} ; - $foo =~ s/$test/$_[1]/ ; - &CTUDebug( "doing in-place edit on " . $_[0] . - " changing '" . $test . "' to '" . - $_[1] . "' yielding '" . $foo . "'\n" ) ; - $newenv{$_[0]} = $foo ; - } else { - &CTUDebug( "'" . $test . "' did not appear in $_[0]." . - " Simple edit\n" ) ; - $dosimple = 1 ; - } - } - } else { - &CTUDebug( "new value does not contain root '" . $_[2] . - "', can do simple edit\n" ) ; - # don't have to sweat in-place edits - $dosimple = 1 ; - } - } - if ( $dosimple ) { - if ( $newenv{$_[0]} eq "" ) { - &CTUDebug( "no pre-existing value in " . $_[0] . - " setting it to '" . $_[1] . "'\n" ) ; - $newenv{$_[0]} = $_[1] ; - } elsif ( $envpostpend{$_[0]} ) { - &CTUDebug( "post-pending '" . $_[1] . "' to " . $_[0] . - "\n" ) ; - $newenv{$_[0]} = $newenv{$_[0]} . " " . $_[1] ; - } elsif ( $envpostpendexceptions{$_[0]}{$_[1]} ) { - &CTUDebug( "post-pending (by exception) '" . $_[1] . "' to '" . $_[0] . - "'\n" ) ; - $newenv{$_[0]} = $newenv{$_[0]} . " " . $_[1] ; - } else { - &CTUDebug( "pre-pending '" . $_[1] . "' to " . $_[0] . - "\n" ) ; - $newenv{$_[0]} = $_[1] . " " . $newenv{$_[0]} ; - } - } - } -} - -require "$tool/built/include/ctcm.pl" ; - -# given the project and flavor, build the lists of variables to set/modify -# input is in: -# $_[0] = project -# $_[1] = flavor -# $_[2] = is some kind of default? -# -# output is in: -# return value is config line -# %newenv = an image of those parts of the environment we want to change -# %envsep = seperator -# %envcmd = set or setenv -# %envdo = direct commands to add to attach script -# %envpostpend = flag that variable should be postpended -sub CTAttachCompute { - &CTUDebug( "in CTAttachCompute\n" ) ; - local( $done ) = 0 ; - local( $flav ) = $_[1] ; - local( $prevflav ) = &CTQueryProj( $_[0] ) ; - local( $spec ) ; - local( $root ) ; - if ( $_[2] && ( $prevflav ne "" )) { - # want some form of default attachment, and are already attached. Short - # circuit. - $done = 1 ; - } - - # - # choose real flavor and find/validate root - # - while ( ! $done ) { - $spec = &CTResolveSpec( $_[0], $flav ) ; - #print STDERR "spec line '" . $spec . "' flav: '" . $flav ."'\n"; - &CTUDebug( "spec line = '$spec'\n" ) ; - if ( $spec ne "" ) { - $root = &CTComputeRoot( $_[0], $flav, $spec ) ; - &CTCMSetup( $_[0], $spec, $flav ) ; - if ( -e $root ) { - $done = 1 ; - } - } else { - print STDERR "could not resolve '" . $flav . "'\n" ; - $done = 1 ; - } - if (( ! $done ) && $_[2] ) { - if ( $flav eq "install" ) { - # oh my! are we ever in trouble - # want some sort of default, but couldn't get to what we wanted - print STDERR "ctattach to install failed\n" ; - $spec = "" ; - $done = 1 ; - } elsif ( $flav eq "release" ) { - $flav = "install" ; - } elsif ( $flav eq "ship" ) { - $flav = "release" ; - } else { - $flav = "install" ; - } - } elsif ( ! $done ) { - $spec = "" ; - print STDERR "resolved '" . $flav . "' but '" . $root . - "' does not exist\n" ; - $done = 1 ; - } - } - - # - # start real work - # - if ( $spec ne "" ) { - local( $proj ) = $_[0] ; - $proj =~ tr/a-z/A-Z/ ; - local( $item ) ; - - # we scan the .init file first because if there are needed sub-attaches - # they must happen before the rest of our work - local( $init ) = "$root/built/etc/$_[0].init" ; - local( %localmod ); - local( %localset ); - local( %localsep ); - local( %localcmd ); - local( %localdo ); - local( $localdocnt ) = 0 ; - local( %localpost ); - local( %localpostexceptions ) = () ; - if ( -e $init ) { - &CTUDebug( "scanning " . $_[0] . ".init\n" ) ; - local( @linesplit ) ; - local( $linetmp ) ; - local( $loop ) ; - local( $looptmp ) ; - local( *INITFILE ) ; - open( INITFILE, "< $init" ) ; - while ( ) { - s/\n$// ; - @linesplit = split( /\#/ ) ; - $_ = $linesplit[0] ; - if ( $_ =~ /^MODABS/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - $linesplitjoin = join( " ", @linesplit ) ; - if ( $linesplit[0] eq "-" ) { - shift( @linesplit ) ; - $linesplitjoin = join( " ", @linesplit ) ; - $localpostexceptions{$linetmp}{$linesplitjoin} = 1 ; - &CTUDebug( "Creating post-pend exception for '" . - $linetmp . "':'" . $linesplitjoin . "'\n" ) ; - } - if ( $localmod{$linetmp} eq "" ) { - $localmod{$linetmp} = $linesplitjoin ; - } else { - $localmod{$linetmp} = $localmod{$linetmp} . " " . - $linesplitjoin ; - } - } elsif ( $_ =~ /^MODREL/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - $postexception = 0 ; - foreach $loop ( @linesplit ) { - if ( $loop eq "-" ) { - $postexception = 1 ; - next ; - } - $looptmp = $root . "/" . &CTUShellEval($loop) ; - if ( $postexception ) { - $localpostexceptions{$linetmp}{$looptmp} = 1 ; - &CTUDebug( "Creating post-pend exception for '" . - $linetmp . "':'" . $looptmp . "'\n" ) ; - } - if ( -e $looptmp ) { - if ( $localmod{$linetmp} eq "" ) { - $localmod{$linetmp} = $looptmp ; - } else { - $localmod{$linetmp} = $localmod{$linetmp} . " " . - $looptmp ; - } - } - } - } elsif ( $_ =~ /^SETABS/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - if ( $localset{$linetmp} eq "" ) { - $localset{$linetmp} = join( " ", @linesplit ) ; - } else { - $localset{$linetmp} = $localset{$linetmp} . " " . - join( " ", @linesplit ) ; - } - } elsif ( $_ =~ /^SETREL/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - foreach $loop ( @linesplit ) { - $looptmp = $root . "/" . &CTUShellEval($loop) ; - if ( -e $looptmp ) { - if ( $localset{$linetmp} eq "" ) { - $localset{$linetmp} = $looptmp ; - } else { - $localset{$linetmp} = $localset{$linetmp} . " " . - $looptmp ; - } - } - } - } elsif ( $_ =~ /^SEP/ ) { - @linesplit = split ; - $localsep{$linesplit[1]} = $linesplit[2] ; - } elsif ( $_ =~ /^CMD/ ) { - @linesplit = split ; - $localcmd{$linesplit[1]} = $linesplit[2] ; - } elsif ( $_ =~ /^DOCSH/ ) { - if ( $shell_type ne "sh" ) { - @linesplit = split ; - shift( @linesplit ) ; - $localdo{$localdocnt} = join( " ", @linesplit ) ; - $localdocnt++ ; - } - } elsif ( $_ =~ /^DOSH/ ) { - if ( $shell_type eq "sh" ) { - @linesplit = split ; - shift( @linesplit ) ; - $localdo{$localdocnt} = join( " ", @linesplit ) ; - $localdocnt++ ; - } - } elsif ( $_ =~ /^DO/ ) { - @linesplit = split ; - shift( @linesplit ) ; - $localdo{$localdocnt} = join( " ", @linesplit ) ; - $localdocnt++ ; - } elsif ( $_ =~ /^POSTPEND/ ) { - @linesplit = split ; - $localpost{$linesplit[1]} = 1 ; - } elsif ( $_ =~ /^ATTACH/ ) { - @linesplit = split ; - shift( @linesplit ) ; - foreach $loop ( @linesplit ) { - push( @attachqueue, $loop ) ; - } - } elsif ( $_ ne "" ) { - print STDERR "Unknown .init directive '$_'\n" ; - } - } - close( INITFILE ) ; - } - - # now handle sub-attaches - &CTUDebug( "performing sub-attaches\n" ) ; - while ( @attachqueue != () ) { - $item = shift( @attachqueue ) ; - &CTUDebug( "attaching to " . $item . "\n" ) ; - &CTAttachCompute( $item, $defflav, 1 ) ; - } - - # now we will do our extentions, then apply the mods from the .init - # file, if any - &CTUDebug( "extending paths\n" ) ; - local( $type ) = &CTSpecType( $spec ) ; - if ( $type eq "vroot" ) { - &CTAttachMod( "PATH", "/usr/atria/bin", $root, $proj ) ; - } - - # For now, we will not check whether the various /bin, /lib, - # /inc directories exist before adding them to the paths. This - # helps when attaching to unitialized trees that do not have - # these directories yet (but will shortly). - - # However, we *will* filter out any trees whose name ends in - # "MODELS". These don't have subdirectories that we care about - # in the normal sense. - if ( ! ( $proj =~ /MODELS$/ ) ) { - - $item = $root . "/built/bin" ; - #if ( -e $item ) { - &CTAttachMod( "PATH", $item, $root, $proj ) ; - #} - - $item = $root . "/built/lib" ; - #if ( -e $item ) { - &CTAttachMod( "PATH", $item, $root, $proj ) ; - &CTAttachMod( "LD_LIBRARY_PATH", $item, $root, $proj ) ; - &CTAttachMod( "DYLD_LIBRARY_PATH", $item, $root, $proj ) ; - #} - - $item = $root . "/built/include" ; - #if ( -e $item ) { - &CTAttachMod( "CT_INCLUDE_PATH", $item, $root, $proj ) ; - #} - - $item = $root . "/built/etc" ; - #if ( -e $item ) { - &CTAttachMod( "ETC_PATH", $item, $root, $proj ) ; - #} - } - - &CTAttachMod( "CTPROJS", $proj . ":" . $flav, $root, $proj ) ; - &CTAttachSet( $proj, $root ) ; - - # run thru the stuff saved up from the .init file - foreach $item ( keys %localsep ) { - $envsep{$item} = $localsep{$item} ; - } - foreach $item ( keys %localpost ) { - $envpostpend{$item} = $localpost{$item} ; - } - %envpostpendexceptions = %localpostexceptions; - foreach $item ( keys %localmod ) { - local( @splitthis ) = split( / +/, $localmod{$item} ) ; - local( $thing ) ; - foreach $thing ( @splitthis ) { - &CTAttachMod( $item, $thing, $root, $proj ) ; - } - } - foreach $item ( keys %localset ) { - &CTAttachSet( $item, $localset{$item} ) ; - } - foreach $item ( keys %localcmd ) { - $envcmd{$item} = $localcmd{$item} ; - } - for ($item = 0; $item < $localdocnt; $item++) { - $envdo{$docnt} = $localdo{$item} ; - $docnt++ ; - } - %envpostpendexceptions = () ; - } - - &CTUDebug( "out of CTAttachCompute\n" ) ; - $spec ; -} - -# write a script to NOT change the environment -# Input is: -# $_[0] = filename -sub CTAttachWriteNullScript { - &CTUDebug( "in CTAttachWriteNullScript\n" ) ; - local( *OUTFILE ) ; - open( OUTFILE, ">$_[0]" ) ; - print OUTFILE "#!/bin/" . $shell_type . " -f\n" ; - print OUTFILE "echo No attachment actions performed\n" ; - print OUTFILE "rm -f $_[0]\n" ; - close( OUTFILE ) ; - &CTUDebug( "out of CTAtachWriteNullScript\n" ) ; -} - -# write a script to setup the environment -# Input is: -# $_[0] = filename -sub CTAttachWriteScript { - &CTUDebug( "in CTAttachWriteScript\n" ) ; - local( *OUTFILE ) ; - open( OUTFILE, ">$_[0]" ) ; - print OUTFILE "#!/bin/" . $shell_type . " -f\n" ; - local( $item ) ; - - foreach $item ( keys %newenv ) { - local( $sep ) = " " ; - if ( $envsep{$item} ne "" ) { - $sep = $envsep{$item} ; - } - local( @splitlist ) = split( / +/, $newenv{$item} ) ; - local( $outval ) = join( $sep, @splitlist ) ; - - if ( $shell_type eq "sh" ) { - print OUTFILE "$item=\"" . $outval . "\"\n" ; - if ( $envcmd{$item} ne "set" ) { - print OUTFILE "export $item\n" ; - } - } else { - if ( $envcmd{$item} ne "" ) { - print OUTFILE $envcmd{$item} . " $item " ; - if ( $envcmd{$item} eq "set" ) { - print OUTFILE "= ( " ; - } - print OUTFILE $outval ; - if ( $envcmd{$item} eq "set" ) { - print OUTFILE ")" ; - } - print OUTFILE "\n" ; - } else { - print OUTFILE "setenv $item \"$outval\"\n" ; - if ( $ctdebug ) { - print OUTFILE "echo setting " . $item . " to '" . $outval . "'\n" ; - } - } - } - } - - #if ( $newenv{"CDPATH"} ne "" ) { - # if ( $shell_type ne "sh" ) { - # print OUTFILE "set cdpath = ( \$" . "CDPATH )\n" ; - # if ( $ctdebug ) { - # print OUTFILE "echo assigning cdpath\n" ; - # } - # } - #} - for ($item = 0; $item < $docnt; $item++) { - print OUTFILE $envdo{$item} . "\n" ; - if ( $ctdebug ) { - print OUTFILE "echo doing '" . $envdo{$item} . "'\n" ; - } - } - if (! $ctdebug) { - print OUTFILE "rm -f $_[0]\n" ; - } else { - print OUTFILE "echo end of script $_[0]\n" ; - print STDERR "no self-destruct script '" . $_[0] . "'\n" ; - } - close( OUTFILE ) ; - &CTUDebug( "out of CTAttachWriteScript\n" ) ; -} - -1; diff --git a/dtool/src/attach/ctattch.pl.rnd b/dtool/src/attach/ctattch.pl.rnd deleted file mode 100644 index 02edd6103b..0000000000 --- a/dtool/src/attach/ctattch.pl.rnd +++ /dev/null @@ -1,954 +0,0 @@ -require "$tool/built/include/ctutils.pl" ; - -# get list of all projects -sub CTAttachListProj { - if ($ctdebug ne "") { - print STDERR "in CTAttachListProj\n" ; - } - local( $ret ) = "" ; - local( $done ) = 0 ; - local( *DIRFILES ) ; - open( DIRFILES, "(cd /var/etc ; /bin/ls -1 *.vspec ; echo blahblah) |" ) ; - while ( ! $done ) { - $_ = ; - s/\n$// ; - if ( $_ eq "blahblah" ) { - $done = 1 ; - } else { - s/.vspec$// ; - $ret = $ret . " " . $_ ; - } - } - close( DIRFILES ) ; - if ($ctdebug ne "") { - print STDERR "out of CTAttachListProj\n" ; - } - $ret ; -} - -# get list of flavors for a project -# $_[0] = project -sub CTAttachListFlav { - if ($ctdebug) { - print STDERR "in CTAttachListFlav\n" ; - } - local( $ret ) = "" ; - $vobname = $_[0] ; - if ( -e "/var/etc/$_[0].vspec" ) { - local( *SPECFILE ) ; - open( SPECFILE, " ) { - if ( $_ =~ /^VOBNAME/ ) { - @partlist = split( /=/ ) ; - $vobname = $partlist[1] ; - $vobname =~ s/\n$// ; - } else { - @partlist = split( /:/ ) ; - $ret = $ret . " " . $partlist[0] ; - } - } - close( SPECFILE ) ; - } else { - print STDERR "CTAttachListFlav: cannot locate '/var/etc/$_[0]'\n" ; - } - if ($ctdebug) { - print STDERR "out of CTAttachListFlav\n" ; - } - $ret ; -} - -# get the flavor line for the given project -# $_[0] = project -# $_[1] = flavor -sub CTAttachFindFlav { - if ($ctdebug) { - print STDERR "in CTAttachFindFlav\n" ; - } - local( $ret ) = "" ; - $vobname = $_[0] ; - if ( -e "/var/etc/$_[0].vspec" ) { - local( *SPECFILE ) ; - open( SPECFILE, " ) && ! $done ) { - s/\n$// ; - if ( $_ =~ /^VOBNAME/ ) { - @partlist = split( /=/ ) ; - $vobname = $partlist[1] ; - } else { - @partlist = split( /:/ ) ; - if ( $partlist[0] eq $_[1] ) { - $done = 1 ; - $ret = join( " ", @partlist ); - } - } - } - close( SPECFILE ) ; - } else { - print STDERR "CTAttachFindFlav: cannot locate '/var/etc/$_[0]'\n" ; - } - if ($ctdebug) { - if ($ret ne "") { - print STDERR "found flavor " . $_[1] . " of project " . $_[0] . "\n" ; - } else { - print STDERR "did not find flavor " . $_[1] . " of project " . $_[0] . "\n" ; - } - print STDERR "out of CTAttachFindFlav\n" ; - } - $ret ; -} - -# given the project and flavor, resolve the final config line -# $_[0] = project -# $_[1] = flavor -sub CTAttachResolve { - if ($ctdebug) { - print STDERR "in CTAttachResolve\n" ; - } - local( $spec ) = &CTAttachFindFlav( $_[0], $_[1] ) ; - local( $ret ) = "" ; - if ( $spec ne "" ) { - local( @speclist ) ; - @speclist = split( / +/, $spec ) ; - if ( $speclist[1] eq "root" ) { - $ret = join( " " , @speclist ) ; - if ($ctdebug) { - print STDERR "resolved to a 'root'\n" ; - } - } elsif ( $speclist[1] eq "vroot" ) { - if ( $ENV{"HAVE_ATRIA"} ne "" ) { - $ret = join( " " , @speclist ) ; - if ($ctdebug) { - print STDERR "resolved to a 'vroot'\n" ; - } - } - } elsif ( $speclist[1] eq "ref" ) { - local( $tmp ) = &CTUShellEval( $speclist[2] ) ; - if ($ctdebug) { - print STDERR "resolved to a 'ref', recursing\n" ; - } - $ret = &CTAttachResolve( $_[0], $tmp ) ; - } else { - print STDERR "CTAttachResolve: unknown flavor type '$speclist[1]'\n" ; - } - } - if ($ctdebug) { - print STDERR "out of CTAttachResolve\n" ; - } - $ret ; -} - -# given the config line, determine the view name -# $_[0] = config line -sub CTAttachComputeView { - if ($ctdebug) { - print STDERR "in CTAttachComputeView\n" ; - } - local( $ret ) = "" ; - if ( $_[0] ne "" ) { - local( @speclist ) ; - @speclist = split( / +/, $_[0] ) ; - if ( $speclist[1] eq "vroot" ) { - local( $vname ) = $speclist[0] ; - shift( @speclist ) ; - shift( @speclist ) ; - local( $item ) ; - local( @itemlist ) ; - foreach $item ( @speclist ) { - @itemlist = split( /=/, $item ) ; - if ( $itemlist[0] eq "VN" ) { - $vname = $itemlist[1] ; - } - } - $ret = &CTUShellEval( $vname ) ; - } - } - if ($ctdebug) { - print STDERR "config line '" . $_[0] . "' yields view name '" . $ret . "'\n" ; - print STDERR "out of CTAttachComputeView\n" ; - } - $ret ; -} - -# given the config line, determine the branch name -# $_[0] = config line -sub CTAttachComputeBranch { - if ($ctdebug) { - print STDERR "in CTAttachComputeBranch\n" ; - } - local( $ret ) = "" ; - if ( $_[0] ne "" ) { - local( @speclist ) ; - @speclist = split( / +/, $_[0] ) ; - if ( $speclist[1] eq "vroot" ) { - local( $bname ) = &CTAttachComputeView( $_[0] ) ; - shift( @speclist ) ; - shift( @speclist ) ; - local( $item ) ; - local( @itemlist ) ; - foreach $item ( @speclist ) { - @itemlist = split( /=/, $item ) ; - if ( $itemlist[0] eq "BN" ) { - $bname = $itemlist[1] ; - } - } - $ret = &CTUShellEval( $bname ) ; - } - } - if ($ctdebug) { - print STDERR "config line '" . $_[0] . "' yields branch name '" . $ret . "'\n" ; - print STDERR "out of CTAttachComputeBranch\n" ; - } - $ret ; -} - -# given the config line, determine the label name -# $_[0] = config line -sub CTAttachComputeLabel { - if ($ctdebug) { - print STDERR "in CTAttachComputeLabel\n" ; - } - local( $ret ) = "" ; - if ( $_[0] ne "" ) { - local( @speclist ) ; - @speclist = split( / +/, $_[0] ) ; - if ( $speclist[1] eq "vroot" ) { - local( $lname ) = &CTAttachComputeView( $_[0] ) ; - shift( @speclist ) ; - shift( @speclist ) ; - local( $item ) ; - local( @itemlist ) ; - foreach $item ( @speclist ) { - @itemlist = split( /=/, $item ) ; - if ( $itemlist[0] eq "LB" ) { - $lname = $itemlist[1] ; - } - } - $ret = &CTUShellEval( $lname ) ; - $ret =~ tr/a-z/A-Z/ ; - } - } - if ($ctdebug) { - print STDERR "config line '" . $_[0] . "' yields label name '" . $ret . "'\n" ; - print STDERR "out of CTAttachComputeLabel\n" ; - } - $ret ; -} - -# given the project name and config line, determine the root of the project -# $_[0] = project -# $_[1] = config line -sub CTAttachComputeRoot { - if ($ctdebug) { - print STDERR "in CTAttachComputeRoot\n" ; - } - local( $ret ) = "" ; - if ( $_[1] ne "" ) { - local( @speclist ) ; - @speclist = split( / +/, $_[1] ) ; - if ( $speclist[1] eq "root" ) { - $ret = $speclist[2] ; - } elsif ( $speclist[1] eq "vroot" ) { - $ret = &CTAttachComputeView( $_[1] ) ; - $ret = "/view/$ret/vobs/$vobname" ; - } else { - print STDERR "CTAttachComputeRoot: unknown flavor type '$speclist[1]'\n" ; - } - } - if ($ctdebug) { - print STDERR "out of CTAttachComputeRoot\n" ; - } - $ret ; -} - -# given the project name and config line, determine the root of the project as -# needed by the config spec. -# $_[0] = project -# $_[1] = config line -sub CTAttachComputeElemRoot { - if ($ctdebug) { - print STDERR "in CTAttachComputeElemRoot\n" ; - } - local( $ret ) = "" ; - if ( $_[1] ne "" ) { - local( @speclist ) ; - @speclist = split( / +/, $_[1] ) ; - if ( $speclist[1] eq "root" ) { - $ret = $speclist[2] ; - } elsif ( $speclist[1] eq "vroot" ) { - $ret = &CTAttachComputeView( $_[1] ) ; - $ret = "/vobs/$vobname" ; - } else { - print STDERR "CTAttachComputeElemRoot: unknown flavor type '$speclist[1]'\n" ; - } - } - if ($ctdebug) { - print STDERR "out of CTAttachComputeElemRoot\n" ; - } - $ret ; -} - -# do whatever setup is needed for ClearCase -# input is in: -# $_[0] = project -# $_[1] = $spec -sub CTAttachCCSetup { - if ($ctdebug) { - print STDERR "in CTAttachCCSetup\n" ; - } - local( $root ) = &CTAttachComputeElemRoot( $_[0], $_[1] ) ; - local( $view ) = &CTAttachComputeView( $_[1] ) ; - local( $branch ) = &CTAttachComputeBranch( $_[1] ) ; - local( $label ) = &CTAttachComputeLabel( $_[1] ) ; - local( *CTINTERFACE ) ; - local( *TMPFILE ) ; - local( $tmpname ) = "/tmp/config.$$" ; - local( $emitted ) = 0 ; - - if ($ctdebug) { - print STDERR "checking for existance of view '" . $view . "'\n" ; - } - open( CTINTERFACE, "/usr/atria/bin/cleartool lsview $view |" ) ; - $_ = ; - close( CTINTERFACE ) ; - if ( $_ eq "" ) { # need to make the view - if ($ctdebug) { - print STDERR "creating view '" . $view . "'\n" ; - } - system "umask 2 ; /usr/atria/bin/cleartool mkview -tag $view /var/views/$view.vws 2> /dev/null > /dev/null ; /usr/atria/bin/cleartool startview $view 2> /dev/null > /dev/null\n" ; - } elsif ( ! ( $_ =~ /\*/ )) { # need to start the view - if ($ctdebug) { - print STDERR "starting view '" . $view . "'\n" ; - } - system "/usr/atria/bin/cleartool startview $view 2> /dev/null > /dev/null &\n" ; - } - - if ($ctdebug) { - print STDERR "making branch and label types for view " . $view . "\n" ; - } - system "/usr/atria/bin/cleartool mkbrtype -vob /vobs/$vobname -c \"Branch type for the $view view\" $branch 2> /dev/null > /dev/null &\n" ; - system "/usr/atria/bin/cleartool mklbtype -vob /vobs/$vobname -c \"Label type for the $view view\" $label 2> /dev/null > /dev/null &\n" ; - - if ($ctdebug) { - print STDERR "creating/updating the config-spec for view " . $view . "\n" ; - } - open( CTINTERFACE, "/usr/atria/bin/cleartool catcs -tag $view |" ) ; - open( TMPFILE, "> $tmpname" ) ; - while ( ) { - if ( $_ =~ "CHECKEDOUT" ) { - print TMPFILE "$_" ; - } elsif (( $_ =~ /^element \*/ ) && ( $_ =~ "/main/LATEST" ) && - !( $_ =~ /$_[0]/ )) { - if ( ! $emitted ) { - $emitted = 1 ; - print TMPFILE "element $root/... .../$branch/LATEST\n" ; - print TMPFILE "element $root/... $label -mkbranch $branch\n" ; - print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; - } - print TMPFILE "$_" ; - } elsif ( $_ =~ /$_[0]/ ) { - if ( ! $emitted ) { - $emitted = 1 ; - print TMPFILE "element $root/... .../$branch/LATEST\n" ; - print TMPFILE "element $root/... $label -mkbranch $branch\n" ; - print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; - } - } else { - print TMPFILE "$_" ; - } - } - close( CTINTERFACE ) ; - close( TMPFILE ) ; - system "/usr/atria/bin/cleartool setcs -tag $view $tmpname ; rm $tmpname &\n" ; - if ($ctdebug) { - print STDERR "out of CTAttachCCSetup\n" ; - } -} - -# do whatever setup is needed for ClearCase, but do it in the background -# input is in: -# $_[0] = project -# $_[1] = $spec -sub CTAttachCCSetupBG { - if ($ctdebug) { - print STDERR "in CTAttachCCSetupBG\n" ; - } - local( $root ) = &CTAttachComputeElemRoot( $_[0], $_[1] ) ; - local( $view ) = &CTAttachComputeView( $_[1] ) ; - local( $branch ) = &CTAttachComputeBranch( $_[1] ) ; - local( $label ) = &CTAttachComputeLabel( $_[1] ) ; - - system "$tool/bin/ctattachcc $root $view $branch $label $vobname $_[0]\n" ; - - if ($ctdebug) { - print STDERR "out of CTAttachCCSetupBG\n" ; - } -} - -# prepend an entry onto the envmod of the given key. -# input is in: -# $_[0] = key -# $_[1] = data -# -# output is in: -# %envmod = has 'data' prepended at 'key' -sub CTAttachPrependMod { - if ( $envmod{$_[0]} eq "" ) { - $envmod{$_[0]} = $_[1] ; - } else { - $envmod{$_[0]} = $_[1] . " " . $envmod{$_[0]} ; - } -} - -# postpend an entry onto the envmod of the given key. -# input is in: -# $_[0] = key -# $_[1] = data -# -# output is in: -# %envmod = has 'data' postpended at 'key' -sub CTAttachPostpendMod { - if ( $envmod{$_[0]} eq "" ) { - $envmod{$_[0]} = $_[1] ; - } else { - $envmod{$_[0]} = $envmod{$_[0]} . " " . $_[1] ; - } -} - -# pre/post-pend an entry onto the envmod of the given key, as set/controlled -# by envpospend, et al. -# input is in: -# $_[0] = key -# $_[1] = data -# -# output is in: -# %envmod = data pre/post pended at the given key -sub CTAttachAddToMod { - if ($envpostpend{$_[0]} ne "") { - &CTAttachPostpendMod( $_[0], $_[1] ) ; - } else { - &CTAttachPrependMod( $_[0], $_[1] ) ; - } -} - -# prepend the given entry to the envset of the given key -# input is in: -# $_[0] = key -# $_[1] = data -# -# output is in: -# %envset = prepended at key with data -sub CTAttachPrependSet { - local( $sep ) = " " ; - if ( $envsep{$_[0]} ne "" ) { - $sep = $envsep{$_[0]} ; - } - if ($envset{$_[0]} ne "") { - $envset{$_[0]} = $_[1] . $sep . $envset{$_[0]} ; - } else { - $envset{$_[0]} = $_[1] ; - } -} - -# postpend the given entry to the envset of the given key -# input is in: -# $_[0] = key -# $_[1] = data -# -# output is in: -# %envset = postpended at key with data -sub CTAttachPostpendSet { - local( $sep ) = " " ; - if ( $envsep{$_[0]} ne "" ) { - $sep = $envsep{$_[0]} ; - } - if ($envset{$_[0]} ne "") { - $envset{$_[0]} = $envset{$_[0]} . $sep . $_[1] ; - } else { - $envset{$_[0]} = $_[1] ; - } -} - -# pre/post-pend an entry onto the envset of the given key, as set/controlled -# by envpospend, et al. -# input is in: -# $_[0] = key -# $_[1] = data -# -# output is in: -# %envset = data pre/post pended at the given key -sub CTAttachAddToSet { - if ($envpostpend{$_[0]} ne "") { - &CTAttachPostpendSet( $_[0], $_[1] ) ; - } else { - &CTAttachPrependSet( $_[0], $_[1] ) ; - } -} - -$docnt = 0 ; -@attachqueue = () ; - -require "$tool/built/include/ctquery.pl" ; - -# given the project and flavor, build the lists of variables to set/modify -# input is in: -# $_[0] = project -# $_[1] = flavor -# $_[2] = is some kind of default? -# -# output is in: -# return value is config line -# %envmod = environment variables to modify -# %envset = environment variables to outright set -# %envsep = seperator -# %envcmd = set or setenv -# %envdo = direct commands to add to attach script -# %envpostpend = flag that variable should be postpended -sub CTAttachCompute { - if ($ctdebug) { - print STDERR "in CTAttachCompute\n" ; - } - local( $done ) = 0 ; - local( $flav ) = $_[1] ; - local( $prevflav ) = &CTQueryProj( $_[0] ) ; - local( $spec ) ; - local( $root ) ; - if ( $_[2] && ( $prevflav ne "" )) { - # short circuit attaching, we're already there. - $done = 1 ; - } - while ( ! $done ) { - $spec = &CTAttachResolve( $_[0], $flav ) ; - if ( $ctdebug ne "" ) { - print STDERR "spec line = '$spec'\n" ; - } - if ( $spec ne "" ) { - $root = &CTAttachComputeRoot( $_[0], $spec ) ; - if ( -e $root ) { - $done = 1 ; - if ( $spec =~ /vroot/ ) { - &CTAttachCCSetupBG( $_[0], $spec ) ; - } - } elsif ( $spec =~ /vroot/ ) { - &CTAttachCCSetup( $_[0], $spec ) ; - if ( -e $root ) { - $done = 1 ; - } - } - } - if (( ! $done ) && $_[2] ) { - if ( $flav eq "install" ) { - # oh my! are we ever in trouble - print STDERR "you are in a strange alien universe\n" ; - $spec = "" ; - $done = 1 ; - } elsif ( $flav eq "release" ) { - $flav = "install" ; - } elsif ( $flav eq "ship" ) { - $flav = "release" ; - } else { - $flav = "ship" ; - } - } - } - - if ( $spec ne "" ) { - local( $proj ) = $_[0] ; - $proj =~ tr/a-z/A-Z/ ; - local( $view ) = &CTAttachComputeView( $spec ) ; - - if ($ctdebug) { - print STDERR "extending paths\n" ; - } - - &CTAttachAddToMod( "PATH", $root . "/bin" ) ; - &CTAttachAddToMod( "LD_LIBRARY_PATH", $root . "/lib" ) ; - &CTAttachAddToMod( "DYLD_LIBRARY_PATH", $root . "/lib" ) ; - #&CTAttachAddToMod( "CDPATH", $root . "/src/all" ) ; - &CTAttachAddToMod( "CT_INCLUDE_PATH", $root . "/include" ) ; - &CTAttachAddToMod( "DC_PATH", $root . "/etc" ) ; - &CTAttachAddToMod( "PFPATH", $root . "/etc/models" ) ; - &CTAttachAddToMod( "SSPATH", $root . "/lib/ss" ) ; - &CTAttachAddToMod( "STKPATH", $root . "/lib/stk" ) ; - &CTAttachAddToMod( "CTPROJS", $proj . ":" . $flav ) ; - $envset{$proj} = $root; - -# if ( $view ne "" ) { -# &CTAttachCCSetup( $_[0], $spec ) ; -# } - - if ( -e "$root/etc/$_[0].init" ) { - if ($ctdebug) { - print STDERR "scanning .init file\n" ; - } - local( @linesplit ) ; - local( $linetmp ) ; - local( $loop ); - local( *INITFILE ) ; - if ( -x "$root/etc/$_[0].init" ) { - open( INITFILE, "$root/etc/$_[0].init $_[0] $_[1] $root |" ) ; - } else { - open( INITFILE, "< $root/etc/$_[0].init" ) ; - } - while ( ) { - s/\n$// ; - if ( $_ =~ /^MODABS/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - &CTAttachPostpendMod( $linetmp, &CTUShellEval(join(" ", @linesplit))) ; - } elsif ( $_ =~ /^MODREL/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - foreach $loop ( @linesplit ) { - &CTAttachPostpendMod( $linetmp, $root . "/" . &CTUShellEval($loop)) ; - } - } elsif ( $_ =~ /^SETABS/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - &CTAttachPrependSet( $linetmp, &CTUShellEval(join(" ", @linesplit))) ; - } elsif ( $_ =~ /^SETREL/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - foreach $loop ( @linesplit ) { - &CTAttachPrependSet( $linetmp, $root . "/" . &CTUShellEval($loop)) ; - } - } elsif ( $_ =~ /^SEP/ ) { - @linesplit = split ; - $envsep{$linesplit[1]} = $linesplit[2] ; - } elsif ( $_ =~ /^CMD/ ) { - @linesplit = split ; - $envcmd{$linesplit[1]} = $linesplit[2] ; - } elsif ( $_ =~ /^DO/ ) { - @linesplit = split ; - shift( @linesplit ) ; - $envdo{$docnt} = join( " ", @linesplit ) ; - $docnt++ ; - } elsif ( $_ =~ /^POSTPEND/ ) { - @linesplit = split ; - $envpospend{$linesplit[1]} = 1 ; - } elsif ( $_ =~ /^ATTACH/ ) { - @linesplit = split ; - shift( @linesplit ) ; - foreach $loop ( @linesplit ) { - push( @attachqueue, $loop ) ; - } - } else { - print STDERR "Unknown .init directive '$_'\n" ; - } - } - close( INITFILE ) ; - } - - # save mods away until after sub-attach - local( %locmod ) ; - local( $item ) ; - - foreach $item ( keys %envmod ) { - $locmod{$item} = $envmod{$item} ; - delete $envmod{$item} ; - } - - # do sub-attaches - while ( @attachqueue != () ) { - $item = shift( @attachqueue ) ; - &CTAttachCompute( $item, $defflav, 1 ) ; - } - - # restore saved mods and merge them in with existing - foreach $item ( keys %locmod ) { - $envmod{$item} = $locmod{$item} ; - delete $locmod{$item} ; - } - - &CTAttachCheckVars( $_[0], $spec ) ; - } - if ($ctdebug) { - print STDERR "out of CTAttachCompute\n" ; - } - $spec ; -} - -# take a mod list and merge it into set. Uniqueifying as we go. -# input is in: -# $_[0] = mod list -# $_[1] = key -# -# output is: -# %envset = now has the mod line merged in with it. -sub CTAttachMergeToSet { - if ( $ctdebug ) { - print STDERR "trying to add '$_[0]' to '$envset{$_[1]}'\n" ; - } - local( @splitlist ) ; - local( $loop ) ; - local( $sep ) = " " ; - if ( $envsep{$_[1]} ne "" ) { - $sep = $envsep{$_[1]} ; - } - @splitlist = split( / /, $_[0] ) ; - foreach $loop ( @splitlist ) { - if ( ! (( $envset{$_[1]} eq $loop ) || - ( $envset{$_[1]} =~ /^$loop$sep/ ) || - ( $envset{$_[1]} =~ /$sep$loop$/ ) || - ( $envset{$_[1]} =~ /$sep$loop$sep/ ))) { - &CTAttachPostpendSet( $_[1], $loop ) ; - } - } - if ( $ctdebug ) { - print STDERR "yielding '$envset{$_[1]}'\n" ; - } -} - -# Perform cleanup operations on the variable that are going to be set/modified -# eg: -# * check to see if we're already attached to the project, and alter sets -# based on that -# * move mods of pre-existing variables to sets w/ the changes included -# * move mods of non-existing variables to sets -# -# input: -# $_[0] = project -# $_[1] = config line -sub CTAttachCheckVars { - if ($ctdebug) { - print STDERR "in CTAttachCheckVars\n" ; - } - local( $prevflav ) = &CTQueryProj( $_[0] ) ; - local( $proj ) = $_[0] ; - $proj =~ tr/a-z/A-Z/ ; - local( $atria ) = "/usr/atria/bin" ; - if ( $ENV{"HAVE_ATRIA"} ne "" ) { - if ( !( $ENV{"PATH"} =~ /$atria/ )) { - $envmod{"PATH"} = "$atria " . $envmod{"PATH"} ; - } - } - if ( $prevflav ne "" ) { # are already attached to the project - if ( $ctdebug ne "" ) { - print STDERR "am already attached\n" ; - } - local( $prevspec ) = &CTAttachResolve( $_[0], $prevflav ) ; - local( $prevroot ) = &CTAttachComputeRoot( $_[0], $prevspec ) ; - local( $root ) = &CTAttachComputeRoot( $_[0], $_[1] ) ; - local( $loop ) ; - local( $item ) ; - local( @splitlist ) ; - local( $modsave ) ; - foreach $item ( keys %envmod ) { - if ( $ENV{$item} ne "" ) { - if ( $ctdebug ne "" ) { - print STDERR "'$item' is already in the environment\n" ; - } - if ( $item eq "CTPROJS" ) { - local( $prevmark ) = $proj . ":" . $prevflav ; - local( $curmark ) = $envmod{$item} ; - if ( $ctdebug ne "" ) { - print STDERR "changing '$prevmark' to '$curmark' yielding " ; - } - if ( ! $gotenv{$item} ) { - $envset{$item} = $ENV{$item} ; - } - $envset{$item} =~ s/$prevmark/$curmark/ ; - if ( $ctdebug ne "" ) { - print STDERR "'$envset{$item}'\n" ; - } - delete $envmod{$item} ; - } else { - local( $src ) ; - if ( $gotenv{$item} ) { - $src = $envset{$item} ; - } else { - $src = $ENV{$item} ; - } - if ( $envsep{$item} ne "" ) { - @splitlist = split( $envsep{$item}, $src ) ; - } else { - @splitlist = split( / +/, $src ) ; - } - $modsave = $envmod{$item} ; - delete $envmod{$item} ; - foreach $loop ( @splitlist ) { - $loop =~ s/$prevroot/$root/ ; - &CTAttachPostpendMod( $item, $loop ) ; - } - if ( $ctdebug ne "" ) { - print STDERR "env '$src' -> '$envmod{$item}'\n" ; - } - @splitlist = split( / +/, $modsave ) ; - foreach $loop ( @splitlist ) { - if ( ! (( $envmod{$item} eq $loop ) || - ( $envmod{$item} =~ /^$loop / ) || - ( $envmod{$item} =~ / $loop$/ ) || - ( $envmod{$item} =~ / $loop / ))) { - &CTAttachAddToMod( $item, $loop ) ; - } - } - if ( $ctdebug ne "" ) { - print STDERR "env final = '$envmod{$item}'\n" ; - } - } - } - if ( $envmod{$item} ne "" ) { - $envset{$item} = $envmod{$item} ; - if ( $envsep{$item} ne "" ) { - $envset{$item} =~ s/ /$envsep{$item}/g ; - } - # &CTAttachMergeToSet( $envmod{$item}, $item ) ; - delete $envmod{$item} ; - $gotenv{$item} = 1 ; - } - } - } else { # not already attached. mods -> sets - if ( $ctdebug ne "" ) { - print STDERR "am not already attached\n" ; - } - local( $item ) ; - local( $loop ) ; - local( $modsave ) ; - local( @splitlist ) ; - foreach $item ( keys %envmod ) { - if ( $ENV{$item} ne "" ) { - local( $src ) ; - if ( $gotenv{$item} ) { - $src = $envset{$item} ; - } else { - $src = $ENV{$item} ; - } - if ( $envsep{$item} ne "" ) { - @splitlist = split( $envsep{$item}, $src ) ; - } else { - @splitlist = split( / +/, $src ) ; - } - $modsave = $envmod{$item} ; - delete $envmod{$item} ; - foreach $loop ( @splitlist ) { - &CTAttachPostpendMod( $item, $loop ) ; - } - if ( $ctdebug ne "" ) { - print STDERR "env '$src' -> '$envmod{$item}'\n" ; - } - @splitlist = split( / +/, $modsave ) ; - foreach $loop ( @splitlist ) { - if ( ! (( $envmod{$item} eq $loop ) || - ( $envmod{$item} =~ /^$loop / ) || - ( $envmod{$item} =~ / $loop$/ ) || - ( $envmod{$item} =~ / $loop / ))) { - &CTAttachAddToMod( $item, $loop ) ; - } - } - if ( $ctdebug ne "" ) { - print STDERR "env final = '$envmod{$item}'\n" ; - } - } - $envset{$item} = $envmod{$item} ; - if ( $envsep{$item} ne "" ) { - $envset{$item} =~ s/ /$envsep{$item}/g ; - } - # &CTAttachMergeToSet( $envmod{$item}, $item ) ; - delete $envmod{$item} ; - $gotenv{$item} = 1 ; - } - } - if ($ctdebug) { - print STDERR "out of CTAttachCheckVars\n" ; - } -} - -# write a script to NOT change the environment -# Input is: -# $_[0] = filename -sub CTAttachWriteNullScript { - if ($ctdebug) { - print STDERR "in CTAttachWriteNullScript\n" ; - } - local( *OUTFILE ) ; - open( OUTFILE, ">$_[0]" ) ; - print OUTFILE "#!/bin/csh -f\n" ; - print OUTFILE "echo No attachment actions performed\n" ; - print OUTFILE "/sbin/rm $_[0]\n" ; - close( OUTFILE ) ; - if ($ctdebug) { - print STDERR "out of CTAtachWriteNullScript\n" ; - } -} - -# write a script to setup the environment -# Input is: -# $_[0] = filename -sub CTAttachWriteScript { - if ($ctdebug) { - print STDERR "in CTAttachWriteScript\n" ; - } - local( *OUTFILE ) ; - open( OUTFILE, ">$_[0]" ) ; - print OUTFILE "#!/bin/csh -f\n" ; - local( $item ) ; - foreach $item ( keys %envset ) { - if ( $envcmd{$item} ne "" ) { - print OUTFILE $envcmd{$item} . " $item " ; - if ( $envcmd{$item} eq "set" ) { - print OUTFILE "= " ; - } - print OUTFILE $envset{$item} . "\n" ; - } else { - print OUTFILE "setenv $item \"$envset{$item}\"\n" ; - } - } - foreach $item ( keys %envmod ) { - print STDERR "SHOULD NOT BE HERE\n" ; - if ( $envcmd{$item} ne "" ) { - print OUTFILE $envcmd{$item} . " $item " ; - if ( $envcmd{$item} eq "set" ) { - print OUTFILE "= ( " ; - } else { - print OUTFILE "\"" ; - } - } else { - print OUTFILE "setenv $item \"" ; - } - if ( $envsep{$item} ne "" ) { - @itemlist = split( / +/, $envmod{$item} ) ; - foreach $tmp ( @itemlist ) { - print OUTFILE $tmp . $envsep{$item} ; - } - } else { - print OUTFILE $envmod{$item} ; - } - if ( $envcmd{$item} ne "" ) { - if ( $envcmd{$item} eq "set" ) { - print OUTFILE ")" ; - } else { - print OUTFILE "\"" ; - } - print OUTFILE "\n" ; - } else { - print OUTFILE $ENV{$item} . "\"\n" ; - } - } - #if (( $envset{"CDPATH"} ne "" ) || ( $envmod{"CDPATH"} ne "" )) { - # print OUTFILE "set cdpath = ( \$" . "CDPATH )\n" ; - #} - foreach $item ( keys %envdo ) { - print OUTFILE $envdo{$item} . "\n" ; - } - if (! $ctdebug) { - print OUTFILE "/sbin/rm $_[0]\n" ; - } else { - print STDERR "no self-destruct script '" . $_[0] . "'\n" ; - } - close( OUTFILE ) ; - if ($ctdebug) { - print STDERR "out of CTAttachWriteScript\n" ; - } -} - -1; diff --git a/dtool/src/attach/ctccase.pl b/dtool/src/attach/ctccase.pl deleted file mode 100644 index b02c2c59b1..0000000000 --- a/dtool/src/attach/ctccase.pl +++ /dev/null @@ -1,430 +0,0 @@ -# given the config line, determine the view name -# $_[0] = config line -# $_[1] = flavor -# $_[2] = project -sub CTAttachComputeView { - &CTUDebug( "in CTAttachComputeView\n" ) ; - local( $ret ) = &CTResolveSpecName( $_[2], $_[1] ) ; - local( $options ) = &CTSpecOptions( $_[0] ) ; - if ( $options ne "" ) { - local( $name ) = &CTSpecFindOption( $options, "name" ) ; - if ( $name ne "" ) { - &CTUDebug( "found a name '" . $name . "'\n" ) ; - $ret = $name ; - } else { - &CTUDebug( "no name option found, going with default\n" ) ; - } - } - &CTUDebug( "config line '" . $_[0] . "' yields view name '" . $ret . - "'\n" . "out of CTAttachComputeView\n" ) ; - $ret ; -} - -# given the config line, determine the branch name -# $_[0] = config line -# $_[1] = flavor -# $_[2] = project -sub CTAttachComputeBranch { - &CTUDebug( "in CTAttachComputeBranch\n" ) ; - local( $ret ) = &CTAttachComputeView( $_[0], $_[1], $_[2] ) ; - &CTUDebug( "config line '" . $_[0] . "' yields branch name '" . $ret . - "'\n" . "out of CTAttachComputeBranch\n" ) ; - $ret ; -} - -# given the config line, determine the label name -# $_[0] = config line -# $_[1] = flavor -# $_[2] = project -sub CTAttachComputeLabel { - &CTUDebug( "in CTAttachComputeLabel\n" ) ; - local( $ret ) = &CTAttachComputeView( $_[0], $_[1], $_[2] ) ; - $ret =~ tr/a-z/A-Z/ ; - &CTUDebug( "config line '" . $_[0] . "' yields label name '" . $ret . - "'\n" . "out of CTAttachComputeLabel\n" ) ; - $ret ; -} - -# given the project name and config line, determine the root of the project as -# needed by the config spec. -# $_[0] = project -# $_[1] = config line -# $_[2] = flavor -sub CTAttachComputeElemRoot { - &CTUDebug( "in CTAttachComputeElemRoot\n" ) ; - local( $ret ) = "/vobs/$_[0]" ; - &CTUDebug( "out of CTAttachComputeElemRoot\n" ) ; - $ret ; -} - -# do whatever setup is needed for ClearCase -# input is in: -# $_[0] = project -# $_[1] = $spec -# $_[2] = flavor -sub CTAttachCCSetup { - &CTUDebug( "in CTAttachCCSetup\n" ) ; - local( $root ) = &CTAttachComputeElemRoot( $_[0], $_[1], $_[2] ) ; - local( $view ) = &CTAttachComputeView( $_[1], $_[2], $_[0] ) ; - local( $branch ) = &CTAttachComputeBranch( $_[1], $_[2], $_[0] ) ; - local( $label ) = &CTAttachComputeLabel( $_[1], $_[2], $_[0] ) ; - local( *CTINTERFACE ) ; - local( *TMPFILE ) ; - local( $tmpname ) = "/tmp/config.$$" ; - local( $emitted ) = 0 ; - - &CTUDebug( "checking for existance of view '" . $view . "'\n" ) ; - open( CTINTERFACE, "/usr/atria/bin/cleartool lsview $view |" ) ; - $_ = ; - close( CTINTERFACE ) ; - if ( $_ eq "" ) { # need to make the view - &CTUDebug( "creating view '" . $view . "'\n" ) ; - system "umask 2 ; /usr/atria/bin/cleartool mkview -tag $view /var/views/$view.vws 2> /dev/null > /dev/null ; /usr/atria/bin/cleartool startview $view 2> /dev/null > /dev/null\n" ; - } elsif ( ! ( $_ =~ /\*/ )) { # need to start the view - &CTUDebug( "starting view '" . $view . "'\n" ) ; - system "/usr/atria/bin/cleartool startview $view 2> /dev/null > /dev/null &\n" ; - } - - &CTUDebug( "making branch and label types for view " . $view . "\n" ) ; - system "/usr/atria/bin/cleartool mkbrtype -vob /vobs/$vobname -c \"Branch type for the $view view\" $branch 2> /dev/null > /dev/null &\n" ; - system "/usr/atria/bin/cleartool mklbtype -vob /vobs/$vobname -c \"Label type for the $view view\" $label 2> /dev/null > /dev/null &\n" ; - - &CTUDebug( "creating/updating the config-spec for view " . $view . "\n" ) ; - open( CTINTERFACE, "/usr/atria/bin/cleartool catcs -tag $view |" ) ; - open( TMPFILE, "> $tmpname" ) ; - while ( ) { - if ( $_ =~ "CHECKEDOUT" ) { - print TMPFILE "$_" ; - } elsif (( $_ =~ /^element \*/ ) && ( $_ =~ "/main/LATEST" ) && - !( $_ =~ /$_[0]/ )) { - if ( ! $emitted ) { - $emitted = 1 ; - print TMPFILE "element $root/... .../$branch/LATEST\n" ; - print TMPFILE "element $root/... $label -mkbranch $branch\n" ; - print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; - } - print TMPFILE "$_" ; - } elsif ( $_ =~ /$_[0]/ ) { - if ( ! $emitted ) { - $emitted = 1 ; - print TMPFILE "element $root/... .../$branch/LATEST\n" ; - print TMPFILE "element $root/... $label -mkbranch $branch\n" ; - print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; - } - } else { - print TMPFILE "$_" ; - } - } - close( CTINTERFACE ) ; - close( TMPFILE ) ; - system "/usr/atria/bin/cleartool setcs -tag $view $tmpname ; rm -f $tmpname &\n" ; - &CTUDebug( "out of CTAttachCCSetup\n" ) ; -} - -# do whatever setup is needed for ClearCase, but do it in the background -# input is in: -# $_[0] = project -# $_[1] = $spec -# $_[2] = flavor -sub CTAttachCCSetupBG { - &CTUDebug( "in CTAttachCCSetupBG\n" ) ; - local( $root ) = &CTAttachComputeElemRoot( $_[0], $_[1], $_[2] ) ; - local( $view ) = &CTAttachComputeView( $_[1], $_[2], $_[0] ) ; - local( $branch ) = &CTAttachComputeBranch( $_[1], $_[2], $_[0] ) ; - local( $label ) = &CTAttachComputeLabel( $_[1], $_[2], $_[0] ) ; - - system "$tool/bin/ctattachcc $root $view $branch $label $vobname $_[0]\n" ; - - &CTUDebug( "out of CTAttachCCSetupBG\n" ) ; -} - -# given a possibly empty string, format it into a comment or -nc -# input is in: -# $_[0] = possible comment string -# -# output is: -# string for use by ClearCase functions -sub CTCcaseFormatComment { - local( $ret ) = "" ; - if ( $_[0] eq "" ) { - $ret = "-nc" ; - } else { - $ret = "-c \"" . $_[0] . "\"" ; - } - $ret ; -} - -# make a versioned directory -# input is in: -# $_[0] = directory to create -# $_[1] = curr dir -# $_[2] = possible comment -# -# output: -# return success or failure -sub CTCcaseMkdir { - &CTUDebug( "in CTCcaseMkdir\n" ) ; - local( $ret ) = 0 ; - local( $dir ) = $_[0] ; - if ( ! ( $dir =~ /^\// )) { - $dir = $_[1] . "/" . $dir ; - } - local( $comment) = &CTCcaseFormatComment( $_[2] ) ; - # first we have to check out the parent directory - local( @alist ) = split( /\//, $dir ) ; - pop( @alist ) ; - local( $parent ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $dir . "' is '" . $parent . "'\n" ) ; - $ret = system( "cleartool co -nc $parent\n" ) ; - if ( $ret == 0 ) { - # now make the dir - $ret = &CTURetCode( system( "cleartool mkdir " . $comment . - " $dir\n" )) ; - } else { - $ret = 0 ; - } - &CTUDebug( "out of CTCcaseMkdir\n" ) ; - $ret ; -} - -# make a versioned element -# input is in: -# $_[0] = element to version -# $_[1] = curr dir -# $_[2] = possible comment -# $_[3] = possible eltype -# -# output: -# return success or failure -sub CTCcaseMkelem { - &CTUDebug( "in CTCcaseMkelem\n" ) ; - local( $ret ) = 0 ; - local( $elem ) = $_[0] ; - if ( ! ( $elem =~ /^\// )) { - $elem = $_[1] . "/" . $elem ; - } - local( $comment) = &CTCcaseFormatComment( $_[2] ) ; - local( $eltype ) = $_[3] ; - if ( $eltype ne "" ) { - $eltype = "-eltype " . $eltype ; - } - # first we have to check out the parent directory - local( @alist ) = split( /\//, $elem ) ; - pop( @alist ) ; - local( $parent ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ; - $ret = system( "cleartool co -nc $parent\n" ) ; - if ( $ret != 0 ) { - &CTUDebug( "checking out the dirctory gave return code: " . $ret . - "\n" ) ; - $ret = 0 ; - } - # now make the elem - $ret = &CTURetCode( system( "cleartool mkelem " . $comment . " " . - $eltype . " $elem\n" )) ; - &CTUDebug( "out of CTCcaseMkelem\n" ) ; - $ret ; -} - -# done here so there will be coherence if multiple deltas are done -require "ctime.pl" ; -$timestamp = &ctime(time) ; -$timestamp =~ s/\n$// ; -@timelist = split( /\s+/, $timestamp ) ; -$timestamp = $timelist[2] . $timelist[1] . $timelist[5] . "_" . $timelist[3] ; -$timestamp =~ s/:/_/g ; - -# delta an element -# input is in: -# $_[0] = element to delta -# -# output: -# return success or failure -sub CTCcaseDelta { - require "$tool/built/include/ctdelta.pl" ; - - &CTUDebug( "in CTCcaseDelta\n" ) ; - local( $ret ) = 0 ; - # this is ripped from the old ctdelta script - &CTDeltaCheckin( $_[0] ) ; - local( $ver ) = &CTDeltaGetVersion( $_[0] ) ; - &CTUDebug( "got version '" . $ver . "'\n" ) ; - if ( &CTDeltaOk( $ver )) { - local( @verlist ) = split( /\//, $ver ) ; - pop( @verlist ) ; - pop( @verlist ) ; - local( $ver2 ) = join( "/", @verlist ) ; - &CTUDebug( "ver2 = '" . $ver2 . "'\n" ) ; - &CTDeltaSafeMerge( $_[0], $ver, $ver2 ) ; - system "cleartool checkin -nc $_[0] 2> /dev/null > /dev/null" ; - &CTUDebug( "merge complete, doing branch check\n" ) ; - &CTDeltaBranchCheck( $_[0], $ver, $timestamp ) ; - &CTUDebug( "logging potentially felonious activity for future" . - " incrimination\n" ) ; - &CTDeltaLog( $_[0], $ver, $ver2 ) ; - # better detection needs to be done - $ret = 1 ; - } else { - &CTUDebug( "cannot merge '" . $_[0] . "', no branches.\n" ) ; - } - &CTUDebug( "out of CTCcaseDelta\n" ) ; - $ret ; -} - -# checkout an element -# input is in: -# $_[0] = element to checkout -# $_[1] = possible comment -# -# output: -# return success or failure -sub CTCcaseCheckout { - &CTUDebug( "in CTCcaseCheckout\n" ) ; - local( $comment) = &CTCcaseFormatComment( $_[1] ) ; - local( $ret ) = &CTURetCode( system( "cleartool co " . $comment . - " $_[0]\n" )) ; - &CTUDebug( "out of CTCcaseCheckout\n" ) ; - $ret ; -} - -# checkin an element -# input is in: -# $_[0] = element to checkin - -# -# output: -# return success or failure -sub CTCcaseCheckin { - &CTUDebug( "in CTCcaseCheckin\n" ) ; - local( $comment) = &CTCcaseFormatComment( $_[1] ) ; - local( $ret ) = &CTURetCode( system( "cleartool ci " . $comment . - " $_[0]\n" )) ; - &CTUDebug( "out of CTCcaseCheckin\n" ) ; - $ret ; -} - -# uncheckout an element -# input is in: -# $_[0] = element to uncheckout -# -# output: -# return success or failure -sub CTCcaseUncheckout { - require "$tool/built/include/unco.pl" ; - &CTUDebug( "in CTCcaseUncheckout\n" ) ; - local( $ret ) = 1 ; - # need better error checking on this - system( "cleartool unco -rm $_[0]\n" ) ; - &CTUncoDoIt( $_[0] ) ; - &CTUDebug( "out of CTCcaseUncheckout\n" ) ; - $ret ; -} - -# figure out what all I have checked out or on my branch -# input is in: -# $_[0] = project -# $_[1] = flavor -# $_[2] = spec line -# -# output: -# return a \n serperated list of elements checked out -sub CTCcaseIHave { - &CTUDebug( "in CTCcaseIHave\n" ) ; - local( $ret ) = "" ; - local( $branch ) = &CTAttachComputeBranch( $_[2], $_[1], $_[0] ) ; - local( $root ) = &CTProjRoot( $_[0] ) ; - local( *OUTPUT ) ; - open( OUTPUT, "cleartool find " . $root . " -element \"brtype(" . - $branch . ")\" -nxn -print |" ) ; - while ( ) { - $ret = $ret . $_ ; - } - close( OUTPUT ) ; - &CTUDebug( "out of CTCcaseIHave\n" ) ; - $ret ; -} - -# remove a versioned element -# input is in: -# $_[0] = element to remove -# $_[1] = curr dir -# -# output: -# return success or failure -sub CTCcaseRmElem { - &CTUDebug( "in CTCcaseRmElem\n" ) ; - local( $ret ) = 0 ; - local( $elem ) = $_[0] ; - if ( ! ( $elem =~ /^\// )) { - $elem = $_[1] . "/" . $elem ; - } - # first we have to check out the parent directory - local( @alist ) = split( /\//, $elem ) ; - pop( @alist ) ; - local( $parent ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ; - $ret = system( "cleartool co -nc $parent\n" ) ; - if ( $ret == 0 ) { - # now nuke the element - $ret = &CTURetCode( system( "cleartool rmname $elem\n" )) ; - } else { - $ret = 0 ; - } - &CTUDebug( "out of CTCcaseRmElem\n" ) ; - $ret ; -} - -# mv a versioned element from one name to another -# input is in: -# $_[0] = from element -# $_[1] = to element -# $_[2] = current directory -# -# output: -# return success or failure -sub CTCcaseMv { - &CTUDebug( "in CTCcaseMv\n" ) ; - local( $ret ) = 0 ; - local( $elem ) = $_[0] ; - if ( ! ( $elem =~ /^\// )) { - $elem = $_[2] . "/" . $elem ; - } - # first we have to check out the parent directory - local( @alist ) = split( /\//, $elem ) ; - pop( @alist ) ; - local( $parent ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ; - local( $elem2 ) = $_[1] ; - if ( ! ( $elem2 =~ /^\// )) { - $elem2 = $_[2] . "/" . $elem2 ; - } - local( @alist ) = split( /\//, $elem2 ) ; - pop( @alist ) ; - local( $parent2 ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $elem2 . "' is '" . $parent2 . - "'\n" ) ; - system( "cleartool co -nc $parent\n" ) ; - system( "cleartool co -nc $parent2\n" ) ; - $ret = &CTURetCode( system( "cleartool mv $elem $elem2\n" )) ; - &CTUDebug( "out of CTCcaseMv\n" ) ; - $ret ; -} - -# build a list of targets -# input is in: -# $_[0] = targets -# -# output: -# return success or failure -sub CTCcaseMake { - &CTUDebug( "in CTCcaseMake\n" ) ; - local( $ret ) = 0 ; - local( $line ) = "clearmake -C gnu " . $_[0] . - " |& grep -v \"^clearmake: Warning: Config\"\n" ; - &CTUDebug( "line = '" . $line . "'\n" ) ; - $ret = &CTURetCode( system( $line )) ; - &CTUDebug( "out of CTCcaseMake\n" ) ; - $ret ; -} - -1; diff --git a/dtool/src/attach/ctci b/dtool/src/attach/ctci deleted file mode 100644 index abf0a5fe03..0000000000 --- a/dtool/src/attach/ctci +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/perl - -sub CTCiUsage { - print STDERR "Usage: ctci [-c \"comment\"] [-nc] element-name [...]\n" ; - print STDERR "Options:\n" ; - print STDERR " -c \"comment\" : provide a comment about this action\n" ; - print STDERR " -nc : expect no comment on this action\n" ; - exit; -} - -if ( $#ARGV < 0 ) { - &CTCiUsage ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "Environment not configured for CTtools" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$comment = "" ; - -$skip = 0 ; - -@files = () ; - -foreach $item ( @ARGV ) { - if ( $skip == 0 ) { - if ( $item eq "-nc" ) { - &CTUDebug( "-nc processed\n" ) ; - } elsif ( $item eq "-c" ) { - $skip = 1 ; - } else { - push( @files, $item ) ; - &CTUDebug( "added '" . $item . "' to files to be processed\n" ) ; - } - } elsif ( $skip == 1 ) { - $comment = $item ; - &CTUDebug( "setting comment to '" . $comment . "'\n" ) ; - $skip = 0 ; - } else { - &CTUDebug( "got to unknown skip value! (" . $skip . ")\n" ) ; - $skip = 0 ; - } -} - - -if ($#files < 0 ) { - &CTCiUsage ; -} - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -foreach $item ( @files ) { - if ( -e $item ) { - if ( ! &CTCMCheckin( $item, $projname, $spec, $comment ) ) { - print STDERR "Could not checkin '$item'\n" ; - } - } else { - print STDERR "No such file '$item'.\n" ; - } -} diff --git a/dtool/src/attach/ctcm.pl b/dtool/src/attach/ctcm.pl deleted file mode 100644 index cfda27e72e..0000000000 --- a/dtool/src/attach/ctcm.pl +++ /dev/null @@ -1,579 +0,0 @@ -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; - -# given a spec line, do the 'correct' setup for it -# input is in: -# $_[0] = project -# $_[1] = spec line -# $_[2] = flavor -sub CTCMSetup { - local( $type ) = &CTSpecType( $_[1] ) ; - if ( $type eq "vroot" ) { - &CTUDebug( "running setup for an atria tree\n" ) ; - if ( $ENV{"HAVE_ATRIA"} eq "yes" ) { - require "$tool/built/include/ctccase.pl" ; - &CTAttachCCSetup( $_[0], $_[1], $_[2] ) ; - } else { - &CTUDebug( "don't HAVE_ATRIA!\n" ) ; - } - # if we don't have atria, and it's a vroot, well.. - } elsif ( $type eq "croot" ) { - &CTUDebug( "running setup for CVS\n" ) ; - require "$tool/built/include/ctcvs.pl" ; - local( $serve ) = &CTCvsServerLine( $_[0], $_[1] ) ; - local( $thing ) = &CTCvsLogin( $serve ) ; - if ( ! $thing ) { - print STDERR "CVS login failed given server line '" . $serve . - "'\n" ; - } - } - # no other types have any work that needs to be done at this time -} - -# given a directory, make sure it's versioned -# input is in: -# $_[0] = directory -# $_[1] = project -# $_[2] = spec line -# $_[3] = comment (optional, "" if none) -# -# output: -# return success or failure -sub CTCMMkdir { - &CTUDebug( "in CTCMMkdir\n" ) ; - local( $ret ) = 0 ; - # first check that the directory is in the project, and is not the root - local( $flav ) = &CTQueryProj( $_[1] ) ; - local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; - local( $pwd ) = &CTUCurrDir() ; - local( $isok ) = 0 ; - if ( $_[0] =~ /^\// ) { - # starts with a /, might not be in the project we are - if (( $_[0] =~ /^$root/ ) && ( $_[0] ne $root )) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - if ( $isok ) { - # ok, it is. Does one already exist? - if ( -e $_[0] ) { - # already one there, nothing to do - &CTUDebug( "directory '" . $_[0] . "' already exists\n" ) ; - $ret = 1 ; - } else { - # now switch off on how to actually do it - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseMkdir( $_[0], $pwd, $_[3] ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolMkdir( $_[0], $pwd, $_[3] ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsMkdir( $_[0], $_[1], $_[2], $_[3] ) ; - } else { - print STDERR "CTCMMkdir::error! got invalid spec type '" . - $type . "'\n" ; - } - } - } else { - print STDERR "directory '" . $_[0] . "' not in project '" . $_[1] . - "' or is the root.\n" ; - } - &CTUDebug( "out of CTCMMkdir\n" ) ; - $ret ; -} - -# given a file, make sure it's versioned -# input is in: -# $_[0] = file -# $_[1] = project -# $_[2] = spec line -# $_[3] = comment (optional, "" if none) -# $_[4] = eltype (optional, "" if none) -# -# output: -# return success or failure -sub CTCMMkelem { - &CTUDebug( "in CTCMMkelem\n" ) ; - local( $ret ) = 0; - # first check that the directory is in the project - local( $flav ) = &CTQueryProj( $_[1] ) ; - local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; - local( $isok ) = 0 ; - local( $pwd ) = &CTUCurrDir() ; - # synth an eltype if there is none - if ( ! -e $_[0] ) { - # need it to already exist - $isok = 0 ; - } else { - if ( -d $_[0] ) { - # wrong command for a directory - $isok = 0 ; - } else { - if ( $_[0] =~ /^\// ) { - # starts with a /, might not be in the project we are - if ( $_[0] =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - } - } - if ( $isok ) { - # now switch off on how to actually do the work - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseMkelem( $_[0], $pwd, $_[3], $_[4] ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolMkelem( $_[0], $pwd, $_[3], $_[4] ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsMkelem( $_[0], $_[1], $_[2], $_[3] ) ; - } else { - print STDERR "CTCMMkelem::error! got invalid spec type '" . - $type . "'\n" ; - } - } - &CTUDebug( "out of CTCMMkelem\n" ) ; - $ret ; -} - -# given an element, delta it in -# input is in: -# $_[0] = element -# $_[1] = project -# $_[2] = spec line -# -# output: -# return success or failure -sub CTCMDelta { - &CTUDebug( "in CTCMDelta\n" ) ; - local( $ret ) = 0 ; - # first check that the element is in the project - local( $flav ) = &CTQueryProj( $_[1] ) ; - local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; - local( $pwd ) = &CTUCurrDir() ; - local( $isok ) = 0 ; - if ( ! -e $_[0] ) { - # can't delta something that doesn't exist - $isok = 0 ; - } else { - if ( $_[0] =~ /^\// ) { - # starts with a /, might not be in the project we are - if ( $_[0] =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - } - if ( $isok ) { - # now switch off on how to actually do the work - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseDelta( $_[0] ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolDelta( $_[0] ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsDelta( $_[0], $_[1], $_[2] ) ; - } else { - print STDERR "CTCMDelta::error! got invalid spec type '" . $type . - "'\n" ; - } - } else { - &CTUDebug( "failed delta pre-checks\n" ) ; - } - &CTUDebug( "out of CTCMDelta\n" ) ; - $ret ; -} - -# given an element, check it out -# input is in: -# $_[0] = element -# $_[1] = project -# $_[2] = spec line -# $_[3] = comment (optional, "" if none) -# -# output: -# return success or failure -sub CTCMCheckout { - &CTUDebug( "in CTCMCheckout\n" ) ; - local( $ret ) = 0 ; - # first check that the element is in the project - local( $flav ) = &CTQueryProj( $_[1] ) ; - local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; - local( $pwd ) = &CTUCurrDir() ; - local( $isok ) = 0 ; - if ( ! -e $_[0] ) { - # can't checkout something that doesn't exist - $isok = 0 ; - } else { - if ( $_[0] =~ /^\// ) { - # starts with a /, might not be in the project we are - if ( $_[0] =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - } - if ( $isok ) { - # now switch off on how to actually do the work - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseCheckout( $_[0], $_[3] ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolCheckout( $_[0], $_[3] ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsCheckout( $_[0], $_[1], $_[2], $_[3] ) ; - } else { - print STDERR "CTCMCheckout::error! got invalid spec type '" . - $type . "'\n" ; - } - } - &CTUDebug( "out of CTCMCheckout\n" ) ; - $ret ; -} - -# given an element, check it in -# input is in: -# $_[0] = element -# $_[1] = project -# $_[2] = spec line -# $_[3] = comment (optional, "" if none) -# -# output: -# return success or failure -sub CTCMCheckin { - &CTUDebug( "in CTCMCheckin\n" ) ; - local( $ret ) = 0 ; - # first check that the element is in the project - local( $flav ) = &CTQueryProj( $_[1] ) ; - local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; - local( $pwd ) = &CTUCurrDir() ; - local( $isok ) = 0 ; - if ( ! -e $_[0] ) { - # can't checkin something that doesn't exist - $isok = 0 ; - } else { - if ( $_[0] =~ /^\// ) { - # starts with a /, might not be in the project we are - if ( $_[0] =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - } - if ( $isok ) { - # now switch off on how to actually do the work - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseCheckin( $_[0], $_[3] ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolCheckin( $_[0], $_[3] ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsCheckin( $_[0], $_[1], $_[2], $_[3] ) ; - } else { - print STDERR "CTCMCheckin::error! got invalid spec type '" . - $type . "'\n" ; - } - } - &CTUDebug( "out of CTCMCheckin\n" ) ; - $ret ; -} - -# given an element, uncheck it out -# input is in: -# $_[0] = element -# $_[1] = project -# $_[2] = spec line -# -# output: -# return success or failure -sub CTCMUncheckout { - &CTUDebug( "in CTCMUncheckout\n" ) ; - local( $ret ) = 0 ; - # first check that the element is in the project - local( $flav ) = &CTQueryProj( $_[1] ) ; - local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; - local( $pwd ) = &CTUCurrDir() ; - local( $isok ) = 0 ; - if ( ! -e $_[0] ) { - # can't uncheckout something that doesn't exist - $isok = 0 ; - } else { - if ( $_[0] =~ /^\// ) { - # starts with a /, might not be in the project we are - if ( $_[0] =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - } - if ( $isok ) { - # now switch off on how to actually do the work - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseUncheckout( $_[0] ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolUncheckout( $_[0] ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsUncheckout( $_[0], $_[1], $_[2] ) ; - } else { - print STDERR "CTCMUncheckout::error! got invalid spec type '" . - $type . "'\n" ; - } - } - &CTUDebug( "out of CTCMUncheckout\n" ) ; - $ret ; -} - -# figure out what all I have checked out in a project -# input is in: -# $_[0] = project -# $_[1] = flavor -# $_[2] = spec line -# -# output: -# return a \n serperated list of elements checked out -sub CTCMIHave { - &CTUDebug( "in CTCMIHave\n" ) ; - local( $ret ) = "" ; - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseIHave( $_[0], $_[1], $_[2] ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolIHave( $_[0], $_[1], $_[2] ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsIHave( $_[0], $_[1], $_[2] ) ; - } else { - print STDERR "CTCMIHave::error! got invalid spec type '" . $type . - "'\n" ; - } - &CTUDebug( "out of CTCMIHave\n" ) ; - $ret ; -} - -# given an element, remove it from the repository -# input is in: -# $_[0] = element -# $_[1] = project -# $_[2] = spec line -# -# output: -# return success or failure -sub CTCMRmElem { - &CTUDebug( "in CTCMRmElem\n" ) ; - local( $ret ) = 0 ; - # first check that the element is in the project - local( $flav ) = &CTQueryProj( $_[1] ) ; - local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; - local( $pwd ) = &CTUCurrDir() ; - local( $isok ) = 0 ; - if ( ! -e $_[0] ) { - # can't rmname something that doesn't exist - $isok = 0 ; - } else { - if ( $_[0] =~ /^\// ) { - # starts with a /, might not be in the project we are - if ( $_[0] =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - } - if ( $isok ) { - # now switch off on how to actually do the work - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseRnElem( $_[0], $pwd ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolRmElem( $_[0], $pwd ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsRmElem( $_[0], $_[1], $_[2] ) ; - } else { - print STDERR "CTCMRmElem::error! got invalid spec type '" . - $type . "'\n" ; - } - } - &CTUDebug( "out of CTCMRmElem\n" ) ; - $ret ; -} - -# move an element from one name to another -# input is in: -# $_[0] = from element -# $_[1] = to element -# $_[2] = project -# $_[3] = spec line -# -# output: -# return success or failure -sub CTCMMv { - &CTUDebug( "in CTCMMv\n" ) ; - local( $ret ) = 0 ; - # first check that the from and to are in the project - local( $flav ) = &CTQueryProj( $_[2] ) ; - local( $root ) = &CTComputeRoot( $_[2], $flav, $_[3] ) ; - local( $pwd ) = &CTUCurrDir() ; - local( $isok ) = 0 ; - if ( $_[0] =~ /^\// ) { - # starts with a /, might not be in the project we are - if ( $_[0] =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - if ( $isok ) { - if ( $_[1] =~ /^\// ) { - # starts with a /, might not be in the project we are - if ( $_[1] =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } else { - # are we sitting in the project? - if ( $pwd =~ /^$root/ ) { - $isok = 1 ; - } else { - $isok = 0 ; - } - } - } - if ( $isok ) { - # now switch off on how to actually do the work - local( $type ) = &CTSpecType( $_[3] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseMv( $_[0], $_[1], $pwd ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolMv( $_[0], $_[1], $pwd ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsMv( $_[0], $_[1], $_[2], $_[3] ) ; - } else { - print STDERR "CTCMMv::error! got invalid spec type '" . - $type . "'\n" ; - } - } - &CTUDebug( "out of CTCMMv\n" ) ; - $ret ; -} - -# give a list of targets, build them -# input is in: -# $_[0] = targets -# $_[1] = project -# $_[2] = spec line -# -# output: -# return success or failure -sub CTCMMake { - &CTUDebug( "in CTCMMake\n" ) ; - local( $ret ) = 0 ; - # now switch off on how to actually do the work - local( $type ) = &CTSpecType( $_[2] ) ; - if ( $type eq "vroot" ) { - require "$tool/built/include/ctccase.pl" ; - $ret = &CTCcaseMake( $_[0] ) ; - } elsif ( $type eq "root" ) { - require "$tool/built/include/ctntool.pl" ; - $ret = &CTNtoolMake( $_[0] ) ; - } elsif ( $type eq "croot" ) { - require "$tool/built/include/ctcvs.pl" ; - $ret = &CTCvsMake( $_[0] ) ; - } else { - print STDERR "CTCMMake::error! got invalid spec type '" . $type . - "'\n" ; - } - &CTUDebug( "out of CTCMMake\n" ) ; - $ret ; -} - -1; diff --git a/dtool/src/attach/ctco b/dtool/src/attach/ctco deleted file mode 100644 index a090e68dba..0000000000 --- a/dtool/src/attach/ctco +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/perl - -sub CTCoUsage { - print STDERR "Usage: ctco [-c \"comment\"] [-nc] element-name [...]\n" ; - print STDERR "Options:\n" ; - print STDERR " -c \"comment\" : provide a comment about this action\n" ; - print STDERR " -nc : expect no comment on this action\n" ; - exit; -} - -if ( $#ARGV < 0 ) { - &CTCoUsage ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "Environment not configured for CTtools" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$comment = "" ; - -$skip = 0 ; - -@files = () ; - -foreach $item ( @ARGV ) { - if ( $skip == 0 ) { - if ( $item eq "-nc" ) { - &CTUDebug( "-nc processed\n" ) ; - } elsif ( $item eq "-c" ) { - $skip = 1 ; - } else { - push( @files, $item ) ; - &CTUDebug( "added '" . $item . "' to files to be processed\n" ) ; - } - } elsif ( $skip == 1 ) { - $comment = $item ; - &CTUDebug( "setting comment to '" . $comment . "'\n" ) ; - $skip = 0 ; - } else { - &CTUDebug( "got to unknown skip value! (" . $skip . ")\n" ) ; - $skip = 0 ; - } -} - -if ( $#files < 0 ) { - &CTCoUsage ; -} - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -foreach $item ( @files ) { - if ( -e $item ) { - if ( ! &CTCMCheckout( $item, $projname, $spec, $comment ) ) { - print STDERR "Could not checkout '$item'\n" ; - } - } else { - print STDERR "No such file '$item'.\n" ; - } -} diff --git a/dtool/src/attach/ctcvs.pl b/dtool/src/attach/ctcvs.pl deleted file mode 100644 index 8e50d75adb..0000000000 --- a/dtool/src/attach/ctcvs.pl +++ /dev/null @@ -1,451 +0,0 @@ -# given a possibly empty string, format it into a comment or -nc -# input is in: -# $_[0] = possible comment string -# -# output is: -# string for use by CVS functions -sub CTCvsFormatComment { - local( $ret ) = "" ; - if ( $_[0] ne "" ) { - $ret = "-m \"" . $_[0] . "\"" ; - } - $ret ; -} - -# given a project and spec line, compute the server line -# input is in: -# $_[0] = project -# $_[1] = spec line -# -# output: -# return a sever line, or "" if not a croot -sub CTCvsServerLine { - &CTUDebug( "in CTCvsServerLine\n" ) ; - local( $ret ) = "" ; - local( $type ) = &CTSpecType( $_[1] ) ; - if ( $type eq "croot" ) { - local( $options ) = &CTSpecOptions( $_[1] ) ; - local( $sline ) = &CTSpecFindOption( $options, "server" ) ; - if ( $sline ne "" ) { - $ret = join( ":", split( /,/, $sline )); - } - } - &CTUDebug( "out of CTCvsServerLine\n" ) ; - $ret ; -} - -# if needed log into a cvs server -# input is in: -# $_[0] = server line -# -# output: -# return success or failure -sub CTCvsLogin { - &CTUDebug( "in CTCvsLogin\n" ) ; - local( $ret ) = 0 ; - &CTUDebug( "server line is '" . $_[0] . "'\n" ) ; - if ( $_[0] ne "" ) { - # ok. we actually have something, lets look in .cvspass - local( $path ) ; - local( *PASSFILE ) ; - if ( $ENV{"PENV"} eq "WIN32" ) { - $path = $ENV{"HOME"} . "/.cvspass" ; - } else { - # $path = "~/.cvspass" ; - $path = $ENV{"HOME"} . "/.cvspass" ; - } - &CTUDebug( "looking for '" . $path . "'\n" ) ; - if ( -e $path ) { - local( $passdone ) = 0 ; - local( $ok ) = 0 ; - open( PASSFILE, "< $path" ) ; - while ( ) { - s/\n$// ; - local( @line ) = split ; - # ok, the server line is in [0] and the password in [1]. - &CTUDebug( "server line from .cvspass is '" . $line[0] . - "'\n" ) ; - if ( $line[0] eq $_[0] ) { - # we're fine, we're already logged in to that - $ret = 1 ; - $passdone = 1; - } - } - if ( ! $passdone ) { - # ran out of lines in the file - local( $line ) = "cvs -d " . $_[0] . " login >/dev/null" ; - &CTUDebug( "about to run '" . $line . "'\n" ) ; - $ret = &CTURetCode( system( $line )) ; - } - } else { - &CTUDebug( $path . " file does not exist\n" ) ; - local( $line ) = "cvs -d " . $_[0] . " login >/dev/null" ; - &CTUDebug( "about to run '" . $line . "'\n" ) ; - $ret = &CTURetCode( system( $line )) ; - } - } - &CTUDebug( "out of CTCvsLogin\n" ) ; - $ret ; -} - -require "$tool/built/include/ctproj.pl" ; - -# add a versioned element to the repository -# input is in: -# $_[0] = element -# $_[1] = project -# $_[2] = spec line -# $_[3] = possible comment -# -# output: -# return success or failure -sub CTCvsAdd { - &CTUDebug( "in CTCvsAdd\n" ) ; - # first we need to 'login' to the repository - local( $comment ) = &CTCvsFormatComment( $_[3] ) ; - local( $serve ) = &CTCvsServerLine( $_[1], $_[2] ) ; - local( $ret ) = &CTCvsLogin( $serve ) ; - if ( $ret ) { - # now issue the add command - local( $root ) = &CTProjRoot( $_[1] ) ; - local( $line ) = "" ; - local( $elem ) = $_[0] ; - if ( $elem =~ /^\// ) { - local( $proj ) = $_[1] ; - $proj =~ tr/a-z/A-Z/ ; - $line = "cd \$" . $proj . "; " ; - $elem =~ s/^$root\/// ; - } - $line = $line . "cvs -d " . $serve . " add " . $comment . " $elem" ; - &CTUDebug( "about to execute '" . $line . "'\n" ) ; - $ret = &CTURetCode( system( $line )) ; - } - &CTUDebug( "out of CTCvsAdd\n" ) ; - $ret ; -} - -# ci a versioned element to the repository -# input is in: -# $_[0] = element -# $_[1] = project -# $_[2] = spec line -# $_[3] = possible comment -# -# output: -# return success or failure -sub CTCvsCi { - &CTUDebug( "in CTCvsCi\n" ) ; - # first we need to 'login' to the repository - local( $comment ) = &CTCvsFormatComment( $_[3] ) ; - local( $serve ) = &CTCvsServerLine( $_[1], $_[2] ) ; - local( $ret ) = &CTCvsLogin( $serve ) ; - if ( $ret ) { - # now issue the add command - local( $root ) = &CTProjRoot( $_[1] ) ; - local( $line ) = "" ; - local( $elem ) = $_[0] ; - if ( $elem =~ /^\// ) { - local ( $proj ) = $_[1] ; - $proj =~ tr/a-z/A-Z/ ; - $line = "cd \$" . $proj . "; " ; - $elem =~ s/^$root\/// ; - } - $line = $line . "cvs -d " . $serve . " ci " . $comment . " $elem" ; - &CTUDebug( "about to execute '" . $line . "'\n" ) ; - $ret = &CTURetCode( system( $line )) ; - } - &CTUDebug( "out of CTCvsCi\n" ) ; - $ret ; -} - -# rm a versioned element from the repository -# input is in: -# $_[0] = element -# $_[1] = project -# $_[2] = spec line -# -# output: -# return success or failure -sub CTCvsRm { - &CTUDebug( "in CTCvsRm\n" ) ; - # first we need to 'login' to the repository - local( $serve ) = &CTCvsServerLine( $_[1], $_[2] ) ; - local( $ret ) = &CTCvsLogin( $serve ) ; - if ( $ret ) { - # now issue the add command - $ret = &CTURetCode( system( "cvs -d " . $serve . " rm $_[0]\n" )) ; - } - &CTUDebug( "out of CTCvsRm\n" ) ; - $ret ; -} - -# make a versioned directory -# input is in: -# $_[0] = directory to create -# $_[1] = project -# $_[2] = spec line -# $_[3] = possible comment -# -# output: -# return success or failure -sub CTCvsMkdir { - &CTUDebug( "in CTCvsMkdir\n" ) ; - local( $ret ) = 0 ; - # first make the dir - $ret = &CTURetCode( system( "mkdir $_[0]\n" )) ; - if ( $ret ) { - # now version it - $ret = &CTCvsAdd( $_[0], $_[1], $_[2], $_[3] ) ; - } else { - &CTUDebug( "could not create directory '" . $_[0] . "'\n" ) ; - $ret = 0 ; - } - &CTUDebug( "out of CTCvsMkdir\n" ) ; - $ret ; -} - -# make a versioned element -# input is in: -# $_[0] = element to version -# $_[1] = project -# $_[2] = spec line -# $_[3] = possible comment -# -# output: -# return success or failure -sub CTCvsMkelem { - &CTUDebug( "in CTCvsMkelem\n" ) ; - # first cvs add the file - local( $ret ) = &CTCvsAdd( $_[0], $_[1], $_[2], $_[3] ) ; - if ( $ret ) { - # now commit it - $ret = &CTCvsCi( $_[0], $_[1], $_[2], $_[3] ) ; - } else { - &CTUDebug( "could not CVS add '" . $_[0] . "'\n" ) ; - $ret = 0 ; - } - &CTUDebug( "out of CTCvsMkelem\n" ) ; - $ret ; -} - -# delta an element -# input is in: -# $_[0] = element to delta -# $_[1] = project -# $_[2] = spec line -# -# output: -# return success or failure -sub CTCvsDelta { - &CTUDebug( "in CTCvsDelta\n" ) ; - local( $ret ) = 0 ; - # for lack of better idea, this is going to be just checkin for now - if ( -d $_[0] ) { - # we don't version directories in CVS - $ret = 1 ; - } else { - $ret = &CTCvsCi( $_[0], $_[1], $_[2] ) ; - } - &CTUDebug( "out of CTCvsDelta\n" ) ; - $ret ; -} - -# checkout an element -# input is in: -# $_[0] = element to checkout -# $_[1] = project -# $_[2] = spec line -# $_[3] = possible comment -# -# output: -# return success or failure -sub CTCvsCheckout { - &CTUDebug( "in CTCvsCheckout\n" ) ; - local( $ret ) = 1 ; - # for my limited understanding of CVS, there doesn't seem to be any - # 'checkout' for it. - &CTUDebug( "out of CTCvsCheckout\n" ) ; - $ret ; -} - -# checkin an element -# input is in: -# $_[0] = element to checkin -# $_[1] = project -# $_[2] = spec line -# $_[3] = possible comment -# -# output: -# return success or failure -sub CTCvsCheckin { - &CTUDebug( "in CTCvsCheckin\n" ) ; - local( $ret ) = 0 ; - if ( -d $_[0] ) { - # we don't version directories in CVS - $ret = 1 ; - } else { - $ret = &CTCvsCi( $_[0], $_[1], $_[2], $_[3] ) ; - } - &CTUDebug( "out of CTCvsCheckin\n" ) ; - $ret ; -} - -# uncheckout an element -# input is in: -# $_[0] = element to uncheckout -# $_[1] = project -# $_[2] = spec line -# -# output: -# return success or failure -sub CTCvsUncheckout { - &CTUDebug( "in CTCvsUncheckout\n" ) ; - local( $ret ) = 0 ; - if ( -d $_[0] ) { - # we don't version directories in CVS - $ret = 1 ; - } else { - $ret = &CTURetCode( system( "rm $_[0]" ) ) ; - if ( $ret ) { - local( $serve ) = &CTCvsServerLine( $_[1], $_[2] ) ; - $ret = &CTCvsLogin( $serve ) ; - if ( $ret ) { - $ret = &CTURetCode( system( "cvs -d " . $serve . " update " . - $_[0] )) ; - } - } - } - &CTUDebug( "out of CTCvsUncheckout\n" ) ; - $ret ; -} - -# figure out what all I have checked out -# input is in: -# $_[0] = project -# $_[1] = flavor -# $_[2] = spec line -# -# output: -# return a \n serperated list of elements checked out -sub CTCvsIHave { - &CTUDebug( "in CTCvsIHave\n" ) ; - local( $ret ) = "" ; - local( $proj ) = $_[0] ; - $proj =~ tr/a-z/A-Z/ ; - local( $line ) = "cd \$" . $proj . "; " ; - local( $serve ) = &CTCvsServerLine( $_[0], $_[2] ) ; - local( $ok ) = &CTCvsLogin( $serve ) ; - if ( $ok ) { - $line = $line . "cvs -n -d " . $serve . " update 2>/dev/null" ; - local( $hold ) = ""; - local( *OUTPUT ) ; - open( OUTPUT, $line . " |" ) ; - while ( ) { - $hold = $hold . $_ ; - } - close( OUTPUT ) ; - local( @lines ) = split( /\n/, $hold ) ; - local( $item ) ; - foreach $item ( @lines ) { - if ( $item =~ /^\?/ ) { - # things that start with a ? are ignored - } elsif ( $item =~ /^cvs/ ) { - # messages from the server are also ignored - } elsif ( $item =~ /^P/ ) { - # new files are ignored - } elsif ( $item =~ /^U/ ) { - # updates are ignored - } elsif ( $item =~ /^M/ ) { - # here's one we modified - local( @foo ) = split( / /, $item ) ; - $ret = $ret . $foo[1] . "\n" ; - } else { - # don't what this means, better complain - local( @foo ) = split( / /, $item ) ; - print STDERR "got unknown update code '" . $foo[0] . - "' for file '" . $foo[1] . "'\n" ; - } - } - } - &CTUDebug( "out of CTCvsIHave\n" ) ; - $ret ; -} - -# remove an element from the repository -# input is in: -# $_[0] = element to uncheckout -# $_[1] = project -# $_[2] = spec line -# -# output: -# return success or failure -sub CTCvsRmElem { - &CTUDebug( "in CTCvsRmElem\n" ) ; - local( $ret ) = 0 ; - if ( -d $_[0] ) { - # CVS doesn't really do this. If there are no files in the directory, - # the next time an update -P is run, it will be deleted. - $ret = 1 ; - } else { - $ret = &CTURetCode( system( "rm $_[0]" ) ) ; - if ( $ret ) { - $ret = &CTCvsRm( $_[0], $_[1], $_[2] ) ; - if ( $ret ) { - $ret = &CTCvsCi( $_[0], $_[1], $_[2] ) ; - } - } - } - &CTUDebug( "out of CTCvsRmElem\n" ) ; - $ret ; -} - -# move a versioned element from one name to another -# input is in: -# $_[0] = from element -# $_[1] = to element -# $_[2] = project -# $_[3] = spec line -# -# output: -# return success or failure -sub CTCvsMv { - &CTUDebug( "in CTCvsMv\n" ) ; - local( $ret ) = 0 ; - if ( -d $_[0] ) { - # don't have code to do directories yet. See pp 54 of the CVS book - $ret = 0 ; - } else { - $ret = &CTURetCode( system( "mv $_[0] $_[1]" ) ) ; - if ( $ret ) { - $ret = &CTCvsRm( $_[0], $_[2], $_[3] ) ; - if ( $ret ) { - $ret = &CTCvsAdd( $_[1], $_[2], $_[3] ); - if ( $ret ) { - $ret = &CTCvsCi( $_[0], $_[2], $_[3] ) ; - if ( $ret ) { - $ret = &CTCvsCi( $_[1], $_[2], $_[3] ) ; - } - } - } - } - } - &CTUDebug( "out of CTCvsMv\n" ) ; - $ret ; -} - -# build a list of targets -# input is in: -# $_[0] = targets -# -# output: -# return success or failure -sub CTCvsMake { - &CTUDebug( "in CTCvsMake\n" ) ; - local( $ret ) = 0 ; - local( $line ) = "make " . $_[0] . "\n" ; - $ret = &CTURetCode( system( $line )) ; - &CTUDebug( "out of CTCvsMake\n" ) ; - $ret ; -} - -1; diff --git a/dtool/src/attach/ctdelta b/dtool/src/attach/ctdelta deleted file mode 100755 index 395c2a37c9..0000000000 --- a/dtool/src/attach/ctdelta +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/perl - -if ($#ARGV < 0) { - exit print "Usage: ctdelta element-name [...]\n" ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "not configured for using ct-tools\n" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$comment = "" ; -$skip = 0 ; - -@files = () ; - -foreach $item ( @ARGV ) { - if ( $skip == 0 ) { - if ( $item eq "-nc" ) { - &CTUDebug( "-nc processed\n" ) ; - } elsif ( $item eq "-c" ) { - $skip = 1 ; - } else { - push( @files, $item ) ; - &CTUDebug( "added '" . $item . "' to files to be processed\n" ) ; - } - } elsif ( $skip == 1 ) { - $comment = $item ; - &CTUDebug( "setting comment to '" . $comment . "'\n" ) ; - $skip = 0 ; - } else { - &CTUDebug( "got to unknown skip value! (" . $skip . ")\n" ) ; - $skip = 0 ; - } -} - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -foreach $item ( @files ) { - if ( -e $item ) { - &CTCMCheckin( $item, $projname, $spec ) ; - if ( ! &CTCMDelta( $item, $projname, $spec ) ) { - print STDERR "Could not delta '$item'\n" ; - } - } else { - print STDERR "No such file '$item'.\n" ; - } -} diff --git a/dtool/src/attach/ctdelta.pl b/dtool/src/attach/ctdelta.pl deleted file mode 100644 index 2b870fa495..0000000000 --- a/dtool/src/attach/ctdelta.pl +++ /dev/null @@ -1,232 +0,0 @@ -# Check in element if needed -# Input is: -# $_[0] = element name -sub CTDeltaCheckin { - local( $cmd ) = "cleartool ci -nc $_[0] 2> /dev/null > /dev/null" ; - system $cmd ; -} - -# get the version of an element -# Input is: -# $_[0] = element name -sub CTDeltaGetVersion { - local( *CMDFILE ) ; - open( CMDFILE, "cleartool describe -short $_[0] |" ) ; - $_ = ; - close( CMDFILE ) ; - s/\n$// ; - s/^.*@@// ; - s/\"$// ; - $_ ; -} - -# Is it ok to try a merge on this version? -# Input is: -# $_[0] = version -sub CTDeltaOk { - local( $ret ) ; - local( @verlist ) ; - @verlist = split( /\//, $_[0] ) ; - pop( @verlist ) ; - if ( $#verlist > 1 ) { - $ret = 1 ; - } else { - $ret = 0 ; - } - $ret ; -} - -# get the comments from a version of an element -# Input is: -# $_[0] = element name -# $_[1] = version -# -# output in: -# @CTDeltaComments -sub CTDeltaGetComments { - local( *CMDFILE ) ; - local( $done ) = 0 ; - local( $end ) = " element type:" ; - local( $tmp ) = "cleartool describe $_[0]" . "@@" . "$_[1] |" ; - open( CMDFILE, $tmp ) ; - $_ = ; - $_ = ; - while ( ! $done ) { - $_ = ; - if ( $_ =~ /^$end/ ) { - $done = 1 ; - } else { - s/^ // ; - s/^ // ; - s/^\"// ; - s/\n$// ; - s/\"$// ; - push( @CTDeltaComments, $_ ) ; - } - } - close( CMDFILE ) ; -} - -# try automatic merge. If it fails, use xmerge -# Input is: -# $_[0] = element name -# $_[1] = source version -# $_[2] = target version -sub CTDeltaSafeMerge { - @CTDeltaComments = (); - &CTDeltaGetComments($_[0], $_[1]); - local( $ret ) ; - $ret = "cleartool checkout -branch $_[2] -nc $_[0] 2> /dev/null > /dev/null" ; - $ret = system $ret ; - if ( $ret != 0 ) { - print STDERR "got return value $ret from checkout on '$_[0]" . "@@" . "$_[2]'\n" ; - exit -1; - } - local( $item ) ; - foreach $item ( @CTDeltaComments ) { - $ret = "cleartool chevent -append -c \"" . $item . "\" $_[0]" . "@@" . "$_[2]" . "/LATEST 2> /dev/null > /dev/null" ; - system $ret ; - } - print STDERR "merging '$_[0]'...\n" ; - $ret = "cleartool merge -abort -to $_[0] -version $_[1] 2> /dev/null > /dev/null" ; - $ret = system $ret ; - if ( $ret != 0 ) { - $ret = system "cleartool xmerge -to $_[0] -version $_[1]" ; - } - if ( ! -d $_[0] ) { - system "rm $_[0]" . ".contrib" ; - } - $ret ; -} - -# test a branch for 'triviality' -# Input is: -# $_[0] = element name -# $_[1] = branch name -# -# Output is: -# true/false -sub CTDeltaTestBranch { - local( *CTCMD ) ; - local( $ret ) ; - local( $done ) = 0 ; - local( $bfrom ) ; - local( @blist ) ; - local( $bto ) ; - local( $bdiff ) ; - local( $blast ) ; - @blist = split( /\//, $_[1] ) ; - pop( @blist ) ; - $ret = join( "/", @blist ) ; - $ret = "cleartool describe $_[0]" . "@@" . "$ret |" ; - open( CTCMD, $ret ) ; - while ( ! $done ) { - $_ = ; - if ( $_ =~ /^ branched from version/ ) { - $done = 1 ; - } - } - close( CTCMD ) ; - s/^ branched from version: // ; - s/\n$// ; - $bfrom = $_ ; - @blist = split( /\//, $_ ) ; - pop( @blist ) ; - push( @blist, "LATEST" ) ; - $ret = join( "/", @blist ) ; - $ret = "cleartool describe $_[0]" . "@@" . "$ret |" ; - open( CTCMD, $ret ) ; - $_ = ; - close( CTCMD ) ; - s/\n$// ; - s/^.*@@// ; - s/\"$// ; - $bto = $_ ; - @blist = split( /\//, $bfrom ) ; - $bfrom = pop( @blist ) ; - @blist = split( /\//, $bto ) ; - $bto = pop( @blist ) ; - $bdiff = $bto - $bfrom ; - $ret = "cleartool describe $_[0]" . "@@" . "$_[1] |" ; - open( CTCMD, $ret ) ; - $_ = ; - close( CTCMD ) ; - s/\n$// ; - s/^.*@@// ; - s/\"$// ; - @blist = split( /\//, $_ ) ; - $blast = pop( @blist ) ; - if (( $bdiff > 1 ) || ( $blast > 1 )) { - $ret = 0 ; - } else { - $ret = 1 ; - } -} - -# check for trivial branch elimination -# Input is: -# $_[0] = element name -# $_[1] = last branch version -# $_[2] = timestamp string -sub CTDeltaBranchCheck { - local( $test ) = &CTDeltaTestBranch( $_[0], $_[1] ) ; - local( $cmd ) ; - local( @blist ) ; - local( $branch ) ; - @blist = split( /\//, $_[1] ) ; - if ( $test ) { - pop( @blist ) ; - $cmd = join( "/", @blist ) ; - $branch = join( "/", @blist ) ; - $cmd = "cleartool rmbranch -force $_[0]" . "@@" . "$cmd 2> /dev/null > /dev/null" ; - print STDERR "deleting branch '$branch'...\n" ; - system $cmd ; - } else { - pop( @blist ) ; - $branch = join( "/", @blist ) ; - $test = pop( @blist ) ; - $test = $test . $_[2] ; - $cmd = "cleartool mkbrtype -c \"non-trivial branch\" $test 2> /dev/null > /dev/null" ; - system $cmd ; - $cmd = "cleartool chtype -c \"renaming non-trivial branch\" $test $_[0]" . "@@" . "$branch 2> /dev/null > /dev/null" ; - print STDERR "renaming branch '$branch'...\n" ; - system $cmd ; - } -} - -# log merge to /usr/local/etc/delta_log -# Input is: -# $_[0] = element name -# $_[1] = source version -# $_[2] = target version -sub CTDeltaLog { - local( *LOGFILE ) ; - local( *CMDFILE ) ; - local( $cmd ) ; - open( LOGFILE, ">>/usr/local/etc/delta_log" ) ; - print LOGFILE $_[0] . ": " . $_[1] . " -> " . $_[2] . " : " ; - if ( $ctdebug ne "" ) { - print STDERR "CTDeltaLog: outputting '" . $_[0] . ": " . $_[1] . " -> " . $_[2] . " : '\n" ; - } - $cmd = "ypmatch `whoami` passwd | cut -d: -f5 |" ; - open( CMDFILE, $cmd ) ; - $_ = ; - s/\n$//; - print LOGFILE $_ . " " ; - if ( $ctdebug ne "" ) { - print STDERR "CTDeltaLog: outputting '" . $_ . " '\n" ; - } - close( CMDFILE ) ; - $cmd = "/bin/date '+%m/%d/%y %H:%M:%S' |" ; - open( CMDFILE, $cmd ) ; - $_ = ; - s/\n$//; - print LOGFILE $_ . "\n" ; - if ( $ctdebug ne "" ) { - print STDERR "CTDeltaLog: outputting '" . $_ . " '\n" ; - } - close( CMDFILE ) ; - close( LOGFILE ) ; -} - -1; diff --git a/dtool/src/attach/ctdelta.pl.rnd b/dtool/src/attach/ctdelta.pl.rnd deleted file mode 100644 index dde5feb574..0000000000 --- a/dtool/src/attach/ctdelta.pl.rnd +++ /dev/null @@ -1,232 +0,0 @@ -# Check in element if needed -# Input is: -# $_[0] = element name -sub CTDeltaCheckin { - local( $cmd ) = "cleartool ci -nc $_[0] 2> /dev/null > /dev/null" ; - system $cmd ; -} - -# get the version of an element -# Input is: -# $_[0] = element name -sub CTDeltaGetVersion { - local( *CMDFILE ) ; - open( CMDFILE, "cleartool describe -short $_[0] |" ) ; - $_ = ; - close( CMDFILE ) ; - s/\n$// ; - s/^.*@@// ; - s/\"$// ; - $_ ; -} - -# Is it ok to try a merge on this version? -# Input is: -# $_[0] = version -sub CTDeltaOk { - local( $ret ) ; - local( @verlist ) ; - @verlist = split( /\//, $_[0] ) ; - pop( @verlist ) ; - if ( $#verlist > 1 ) { - $ret = 1 ; - } else { - $ret = 0 ; - } - $ret ; -} - -# get the comments from a version of an element -# Input is: -# $_[0] = element name -# $_[1] = version -# -# output in: -# @CTDeltaComments -sub CTDeltaGetComments { - local( *CMDFILE ) ; - local( $done ) = 0 ; - local( $end ) = " element type:" ; - local( $tmp ) = "cleartool describe $_[0]" . "@@" . "$_[1] |" ; - open( CMDFILE, $tmp ) ; - $_ = ; - $_ = ; - while ( ! $done ) { - $_ = ; - if ( $_ =~ /^$end/ ) { - $done = 1 ; - } else { - s/^ // ; - s/^ // ; - s/^\"// ; - s/\n$// ; - s/\"$// ; - push( @CTDeltaComments, $_ ) ; - } - } - close( CMDFILE ) ; -} - -# try automatic merge. If it fails, use xmerge -# Input is: -# $_[0] = element name -# $_[1] = source version -# $_[2] = target version -sub CTDeltaSafeMerge { - @CTDeltaComments = (); - &CTDeltaGetComments($_[0], $_[1]); - local( $ret ) ; - $ret = "cleartool checkout -branch $_[2] -nc $_[0] 2> /dev/null > /dev/null" ; - $ret = system $ret ; - if ( $ret != 0 ) { - print STDERR "got return value $ret from checkout on '$_[0]" . "@@" . "$_[2]'\n" ; - exit -1; - } - local( $item ) ; - foreach $item ( @CTDeltaComments ) { - $ret = "cleartool chevent -append -c \"" . $item . "\" $_[0]" . "@@" . "$_[2]" . "/LATEST 2> /dev/null > /dev/null" ; - system $ret ; - } - print STDERR "merging '$_[0]'...\n" ; - $ret = "cleartool merge -abort -to $_[0] -version $_[1] 2> /dev/null > /dev/null" ; - $ret = system $ret ; - if ( $ret != 0 ) { - $ret = system "cleartool xmerge -to $_[0] -version $_[1]" ; - } - if ( ! -d $_[0] ) { - system "rm $_[0]" . ".contrib" ; - } - $ret ; -} - -# test a branch for 'triviality' -# Input is: -# $_[0] = element name -# $_[1] = branch name -# -# Output is: -# true/false -sub CTDeltaTestBranch { - local( *CTCMD ) ; - local( $ret ) ; - local( $done ) = 0 ; - local( $bfrom ) ; - local( @blist ) ; - local( $bto ) ; - local( $bdiff ) ; - local( $blast ) ; - @blist = split( /\//, $_[1] ) ; - pop( @blist ) ; - $ret = join( "/", @blist ) ; - $ret = "cleartool describe $_[0]" . "@@" . "$ret |" ; - open( CTCMD, $ret ) ; - while ( ! $done ) { - $_ = ; - if ( $_ =~ /^ branched from version/ ) { - $done = 1 ; - } - } - close( CTCMD ) ; - s/^ branched from version: // ; - s/\n$// ; - $bfrom = $_ ; - @blist = split( /\//, $_ ) ; - pop( @blist ) ; - push( @blist, "LATEST" ) ; - $ret = join( "/", @blist ) ; - $ret = "cleartool describe $_[0]" . "@@" . "$ret |" ; - open( CTCMD, $ret ) ; - $_ = ; - close( CTCMD ) ; - s/\n$// ; - s/^.*@@// ; - s/\"$// ; - $bto = $_ ; - @blist = split( /\//, $bfrom ) ; - $bfrom = pop( @blist ) ; - @blist = split( /\//, $bto ) ; - $bto = pop( @blist ) ; - $bdiff = $bto - $bfrom ; - $ret = "cleartool describe $_[0]" . "@@" . "$_[1] |" ; - open( CTCMD, $ret ) ; - $_ = ; - close( CTCMD ) ; - s/\n$// ; - s/^.*@@// ; - s/\"$// ; - @blist = split( /\//, $_ ) ; - $blast = pop( @blist ) ; - if (( $bdiff > 1 ) || ( $blast > 1 )) { - $ret = 0 ; - } else { - $ret = 1 ; - } -} - -# check for trivial branch elimination -# Input is: -# $_[0] = element name -# $_[1] = last branch version -# $_[2] = timestamp string -sub CTDeltaBranchCheck { - local( $test ) = &CTDeltaTestBranch( $_[0], $_[1] ) ; - local( $cmd ) ; - local( @blist ) ; - local( $branch ) ; - @blist = split( /\//, $_[1] ) ; - if ( $test ) { - pop( @blist ) ; - $cmd = join( "/", @blist ) ; - $branch = join( "/", @blist ) ; - $cmd = "cleartool rmbranch -force $_[0]" . "@@" . "$cmd 2> /dev/null > /dev/null" ; - print STDERR "deleting branch '$branch'...\n" ; - system $cmd ; - } else { - pop( @blist ) ; - $branch = join( "/", @blist ) ; - $test = pop( @blist ) ; - $test = $test . $_[2] ; - $cmd = "cleartool mkbrtype -c \"non-trivial branch\" $test 2> /dev/null > /dev/null" ; - system $cmd ; - $cmd = "cleartool chtype -c \"renaming non-trivial branch\" $test $_[0]" . "@@" . "$branch 2> /dev/null > /dev/null" ; - print STDERR "renaming branch '$branch'...\n" ; - system $cmd ; - } -} - -# log merge to /var/etc/delta_log -# Input is: -# $_[0] = element name -# $_[1] = source version -# $_[2] = target version -sub CTDeltaLog { - local( *LOGFILE ) ; - local( *CMDFILE ) ; - local( $cmd ) ; - open( LOGFILE, ">>/var/etc/delta_log" ) ; - print LOGFILE $_[0] . ": " . $_[1] . " -> " . $_[2] . " : " ; - if ( $ctdebug ne "" ) { - print STDERR "CTDeltaLog: outputting '" . $_[0] . ": " . $_[1] . " -> " . $_[2] . " : '\n" ; - } - $cmd = "ypmatch `whoami` passwd | cut -d: -f5 |" ; - open( CMDFILE, $cmd ) ; - $_ = ; - s/\n$//; - print LOGFILE $_ . " " ; - if ( $ctdebug ne "" ) { - print STDERR "CTDeltaLog: outputting '" . $_ . " '\n" ; - } - close( CMDFILE ) ; - $cmd = "/bin/date '+%m/%d/%y %H:%M:%S' |" ; - open( CMDFILE, $cmd ) ; - $_ = ; - s/\n$//; - print LOGFILE $_ . "\n" ; - if ( $ctdebug ne "" ) { - print STDERR "CTDeltaLog: outputting '" . $_ . " '\n" ; - } - close( CMDFILE ) ; - close( LOGFILE ) ; -} - -1; diff --git a/dtool/src/attach/ctihave b/dtool/src/attach/ctihave deleted file mode 100755 index c6cfe5267f..0000000000 --- a/dtool/src/attach/ctihave +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/perl - -if ($#ARGV != -1) { - exit print "Usage: ctihave\n" ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "not configured for using CTtools\n" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -if ( $projname eq "" ) { - exit print "Not currently in any project tree\n" ; -} - -$result = &CTCMIHave( $projname, $flav, $spec ) ; -if ( $result ne "" ) { - @splitlist = split( /\n/, $result ) ; - foreach $item ( @splitlist ) { - print $item . "\n" ; - } -} diff --git a/dtool/src/attach/ctmake b/dtool/src/attach/ctmake deleted file mode 100644 index 56326744ab..0000000000 --- a/dtool/src/attach/ctmake +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/perl - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "not configured for using ct-tools\n" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -$line = join( " ", @ARGV ) ; - -if ( ! &CTCMMake( $line, $projname, $spec ) ) { - print STDERR "Could not make '$line'\n" ; -} diff --git a/dtool/src/attach/ctmkdir b/dtool/src/attach/ctmkdir deleted file mode 100644 index 678dd9494e..0000000000 --- a/dtool/src/attach/ctmkdir +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/perl - -sub CTMkDirUsage { - print STDERR "Usage: ctmkdir [-c \"comment\"] [-nc] dir-name [...]\n" ; - print STDERR "Options:\n" ; - print STDERR " -c \"comment\" : provide a comment about this action\n" ; - print STDERR " -nc : expect no comment on this action\n" ; - exit ; -} - -if ( $#ARGV < 0 ) { - &CTMkDirUsage ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "Environment not configured for CTtools" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$comment = "" ; -if ( $ARGV[0] eq "-nc" ) { - shift( @ARGV ) ; - &CTUDebug( "-nc processed\n" ) ; -} -if ( $ARGV[0] eq "-c" ) { - shift( @ARGV ) ; - $comment = $ARGV[0] ; - shift( @ARGV ) ; - &CTUDebug( "setting comment to '" . $comment . "'\n" ) ; -} - -if ( $#ARGV < 0 ) { - &CTMkDirUsage ; -} - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -foreach $item ( @ARGV ) { - if ( -e $item ) { - print STDERR "Name collision on directory '$item'\n" ; - } else { - if ( ! &CTCMMkdir( $item, $projname, $spec, $comment ) ) { - print STDERR "Could name make directory '$item'\n" ; - } - } -} diff --git a/dtool/src/attach/ctmkelem b/dtool/src/attach/ctmkelem deleted file mode 100644 index 3d0018cad7..0000000000 --- a/dtool/src/attach/ctmkelem +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/perl - -sub CTMkElemUsage { - print STDERR "Usage: ctmkelem [-c \"comment\"] [-nc] [-eltype type] element-name [...]\n" ; - print STDERR "Options:\n" ; - print STDERR " -c \"comment\" : provide a comment about this action\n" ; - print STDERR " -nc : expect no comment on this action\n" ; - print STDERR " -eltype type : element type\n" ; - exit ; -} - -if ( $#ARGV < 0 ) { - &CTMkElemUsage ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "Environment not configured for CTtools" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$comment = "" ; -$eltype = "" ; - -$done = 0 ; - -while ( ! $done ) { - $done = 1 ; - if ( $ARGV[0] eq "-nc" ) { - shift( @ARGV ) ; - &CTUDebug( "-nc processed\n" ) ; - $done = 0 ; - } - if ( $ARGV[0] eq "-c" ) { - shift( @ARGV ) ; - $comment = $ARGV[0] ; - shift( @ARGV ) ; - &CTUDebug( "setting comment to '" . $comment . "'\n" ) ; - $done = 0 ; - } - if ( $ARGV[0] eq "-eltype" ) { - shift( @ARGV ) ; - $eltype = $ARGV[0] ; - shift( @ARGV ) ; - &CTUDebug( "setting eltype to '" . $eltype . "'\n" ) ; - $done = 0 ; - } -} - -if ( $#ARGV < 0 ) { - &CTMkElemUsage ; -} - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -foreach $item ( @ARGV ) { - if ( -e $item ) { - if ( -d $item ) { - print STDERR "Cannot mkelem on an existing directory." . - " Ctmkdir it first.\n" ; - } else { - if ( ! &CTCMMkelem( $item, $projname, $spec, $comment, $eltype )) { - print STDERR "Could not make a versioned element of '" . - $item . "'\n" ; - } - } - } else { - print STDERR "No such file '$item'.\n" ; - } -} diff --git a/dtool/src/attach/ctmv b/dtool/src/attach/ctmv deleted file mode 100644 index 6b4985429e..0000000000 --- a/dtool/src/attach/ctmv +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/perl - -if ( $#ARGV != 1 ) { - exit print "Usage: ctmv from-element to-element\n" ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "Environment not configured for CTtools" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -$from = $ARGV[0] ; -$to = $ARGV[1] ; - -if ( -e $from ) { - if ( -e $to ) { - print STDERR "'$to' already exists.\n" ; - } else { - if ( ! &CTCMMv( $from, $to, $projname, $spec ) ) { - } - } -} else { - print STDERR "No such element '$from'.\n" ; -} diff --git a/dtool/src/attach/ctntool.pl b/dtool/src/attach/ctntool.pl deleted file mode 100644 index 6920a3ee23..0000000000 --- a/dtool/src/attach/ctntool.pl +++ /dev/null @@ -1,258 +0,0 @@ -# given a possibly empty string, format it into a comment or -nc -# input is in: -# $_[0] = possible comment string -# -# output is: -# string for use by neartool functions -sub CTNtoolFormatComment { - local( $ret ) = "" ; - if ( $_[0] eq "" ) { - $ret = "-nc" ; - } else { - $ret = "-c \"" . $_[0] . "\"" ; - } - $ret ; -} - -# make a versioned directory -# input is in: -# $_[0] = directory to create -# $_[1] = curr dir -# $_[2] = possible comment -# -# output: -# return success or failure -sub CTNtoolMkdir { - &CTUDebug( "in CTNtoolMkdir\n" ) ; - local( $ret ) = 0 ; - local( $dir ) = $_[0] ; - if ( ! ( $dir =~ /^\// )) { - $dir = $_[1] . "/" . $dir ; - } - local( $comment ) = &CTNtoolFormatComment( $_[2] ) ; - # first we have to check out the parent directory - local( @alist ) = split( /\//, $dir ) ; - pop( @alist ) ; - local( $parent ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $dir . "' is '" . $parent . "'\n" ) ; - $ret = system( "neartool co -nc $parent\n" ) ; - if ( $ret == 0 ) { - # now make the dir - $ret = &CTURetCode( system( "neartool mkdir " . $comment . - " $dir\n" )) ; - } else { - $ret = 0 ; - } - &CTUDebug( "out of CTNtoolMkdir\n" ) ; - $ret ; -} - -# make a versioned element -# input is in: -# $_[0] = element to version -# $_[1] = curr dir -# $_[2] = possible comment -# $_[3] = possible eltype -# -# output: -# return success or failure -sub CTNtoolMkelem { - &CTUDebug( "in CTNtoolMkelem\n" ) ; - local( $ret ) = 0 ; - local( $elem ) = $_[0] ; - if ( ! ( $elem =~ /^\// )) { - $elem = $_[1] . "/" . $elem ; - } - local( $comment ) = &CTNtoolFormatComment( $_[2] ) ; - local( $eltype ) = $_[3] ; - if ( $eltype ne "" ) { - $eltype = "-eltype " . $eltype ; - } - local( $line ) = "neartool mkelem " . $comment . " " . $eltype . " " . - $elem . "\n" ; - &CTUDebug( $line ) ; - $ret = &CTURetCode( system( $line )) ; - &CTUDebug( "out of CTNtoolMkelem\n" ) ; - $ret ; -} - -# delta an element -# input is in: -# $_[0] = element to delta -# -# output: -# return success or failure -sub CTNtoolDelta { - &CTUDebug( "in CTNtoolDelta\n" ) ; - local( $ret ) = 0 ; - # as Dave points out, when working off-line, delta is the same as checkin - $ret = &CTURetCode( system( "neartool ci " . $_[0] )) ; - &CTUDebug( "out of CTNtoolDelta\n" ) ; - $ret ; -} - -# checkout an element -# input is in: -# $_[0] = element to checkout -# $_[1] = possible comment -# -# output: -# return success or failure -sub CTNtoolCheckout { - &CTUDebug( "in CTNtoolCheckout\n" ) ; - local( $ret ) = 0 ; - local( $comment ) = &CTNtoolFormatComment( $_[1] ) ; - if ( ! -d $_[0] ) { - $ret = &CTURetCode( system( "neartool co " . $comment . " " . - $_[0] )) ; - } else { - # neartool doesn't do anything about checking out directories - $ret = 1 ; - } - &CTUDebug( "out of CTNtoolCheckout\n" ) ; - $ret ; -} - -# checkin an element -# input is in: -# $_[0] = element to checkin -# $_[1] = possible comment -# -# output: -# return success or failure -sub CTNtoolCheckin { - &CTUDebug( "in CTNtoolCheckin\n" ) ; - local( $ret ) = 0 ; - local( $comment ) = &CTNtoolFormatComment( $_[1] ) ; - $ret = &CTURetCode( system( "neartool ci " . $comment . " " . $_[0] )) ; - &CTUDebug( "out of CTNtoolCheckin\n" ) ; - $ret ; -} - -# uncheckout an element -# input is in: -# $_[0] = element to uncheckout -# -# output: -# return success or failure -sub CTNtoolUncheckout { - &CTUDebug( "in CTNtoolUncheckout\n" ) ; - local( $ret ) = 0 ; - $ret = &CTURetCode( system( "neartool unco " . $_[0] )) ; - &CTUDebug( "out of CTNtoolUncheckout\n" ) ; - $ret ; -} - -# figure out what all I have checked out -# input is in: -# $_[0] = project -# $_[1] = flavor -# $_[2] = spec line -# -# output: -# return a \n serperated list of elements checked out -sub CTNtoolIHave { - &CTUDebug( "in CTNtoolIHave\n" ) ; - local( $ret ) = "" ; - local( $root ) = &CTProjRoot( $_[0] ) ; - local( *OUTPUT ) ; - open( OUTPUT, "neartool find " . $root . " |" ) ; - while ( ) { - $ret = $ret . $_ ; - } - close( OUTPUT ) ; - &CTUDebug( "out of CTNToolIHave\n" ) ; - $ret ; -} - -# remove a versioned element -# input is in: -# $_[0] = element to remove -# $_[1] = curr dir -# -# output: -# return success or failure -sub CTNtoolRmElem { - &CTUDebug( "in CTNtoolRmElem\n" ) ; - local( $ret ) = 0 ; - local( $elem ) = $_[0] ; - if ( ! ( $elem =~ /^\// )) { - $elem = $_[1] . "/" . $elem ; - } - # first we have to check out the parent directory - local( @alist ) = split( /\//, $elem ) ; - pop( @alist ) ; - local( $parent ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ; - $ret = system( "neartool co -nc $parent\n" ) ; - if ( $ret == 0 ) { - # now nuke the element - $ret = &CTURetCode( system( "neartool rmname $elem\n" )) ; - } else { - $ret = 0 ; - } - &CTUDebug( "out of CTNtoolRmElem\n" ) ; - $ret ; -} - -# mv a versioned element from one name to another -# input is in: -# $_[0] = from element -# $_[1] = to element -# $_[2] = current directory -# -# output: -# return success or failure -sub CTNtoolMv { - &CTUDebug( "in CTNtoolMv\n" ) ; - local( $ret ) = 0 ; - local( $elem ) = $_[0] ; - if ( ! ( $elem =~ /^\// )) { - $elem = $_[2] . "/" . $elem ; - } - # first we have to check out the parent directory - local( @alist ) = split( /\//, $elem ) ; - pop( @alist ) ; - local( $parent ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ; - local( $elem2 ) = $_[1] ; - if ( ! ( $elem2 =~ /^\// )) { - $elem2 = $_[2] . "/" . $elem2 ; - } - @alist = split( /\//, $elem2 ) ; - pop( @alist ) ; - local( $parent2 ) = join( "/", @alist ) ; - &CTUDebug( "parent directory of '" . $elem2 . "' is '" . $parent2 . - "'\n" ) ; - $ret = system( "neartool co -nc $parent\n" ) ; - if ( $ret == 0 ) { - $ret = system( "neartool co -nc $parent2\n" ) ; - if ( $ret == 0 ) { - # now move the element - $ret = &CTURetCode( system( "neartool mv $elem $elem2\n" )) ; - } else { - $ret = 0 ; - } - } else { - $ret = 0 ; - } - &CTUDebug( "out of CTNtoolMv\n" ) ; - $ret ; -} - -# build a list of targets -# input is in: -# $_[0] = targets -# -# output: -# return success or failure -sub CTNtoolMake { - &CTUDebug( "in CTNtoolMake\n" ) ; - local( $ret ) = 0 ; - local( $line ) = "make " . $_[0] . "\n" ; - $ret = &CTURetCode( system( $line )) ; - &CTUDebug( "out of CTNtoolMake\n" ) ; - $ret ; -} - -1; diff --git a/dtool/src/attach/ctproj.pl b/dtool/src/attach/ctproj.pl deleted file mode 100644 index 38f5fd269b..0000000000 --- a/dtool/src/attach/ctproj.pl +++ /dev/null @@ -1,60 +0,0 @@ -require "$tool/built/include/ctutils.pl" ; - -# return the root of the given project. -sub CTProjRoot { - local( $CTPRtmp ) = $_[0] ; - $CTPRtmp =~ tr/a-z/A-Z/ ; - local( $CTPRret ) = $ENV{ $CTPRtmp } ; - $CTPRret ; -} - -# return the package we're currently in. -# input: -# $_[0] = project -sub CTProjPkg { - local( $CTPPret ) = &CTUCurrDir() ; - local( $CTPPtmp ) = $_[0] ; - $CTPPtmp =~ tr/a-z/A-Z/ ; - $CTPPret =~ s/$ENV{ $CTPPtmp }// ; - $CTPPret =~ s/\/src\/// ; - $CTPPret =~ s/\/metalibs\/// ; - $CTPPret ; -} - -# reutrn the project containing the given directory. If no directory is given, -# return the project containing the current directory. -sub CTProj { - local( $CTPdir ) ; - if ($_[0] eq "") { - $CTPdir = &CTUCurrDir() ; - } else { - # provided directory - $CTPdir = $_[0] ; - } - local( $CTPprojs ) = $ENV{"CTPROJS"} ; - local( $CTPdone ) = "" ; - local( @CTPlist ) ; - @CTPlist = split( / /, $CTPprojs ) ; - local( @CTPlist2 ) ; - local( $CTPtry ) ; - while (( $CTPdone eq "" ) && ( @CTPlist != () )){ - # pop the first one off the list - $CTPtmp = $CTPlist[0] ; - shift( @CTPlist ) ; - # split the project from it's flavor - @CTPlist2 = split( /:/, $CTPtmp ); - $CTPtry = &CTProjRoot( $CTPlist2[0] ) ; - # is CTPtry prefix of CTPdir? if so we have our winner - if ( $CTPdir =~ /^$CTPtry/ ) { - $CTPdone = "yep" ; - } - } - if ( $CTPdone eq "" ) { - $CTPtry = "" ; - } else { - $CTPtry = $CTPlist2[0] ; - } - $CTPtry ; -} - -1; diff --git a/dtool/src/attach/ctquery b/dtool/src/attach/ctquery deleted file mode 100755 index 89a2380ed8..0000000000 --- a/dtool/src/attach/ctquery +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/perl - -# acceptable forms: -# ctquery - list all attached projects and flavors -# ctquery project - list the attached flavor of the named project -# ctquery - flavor - list all attached projects who are attached with a -# given flavor - -$projs = $ENV{"CTPROJS"} ; -@projlist = split( / +/, $projs ) ; - -if ( $#ARGV == -1 ) { - # list all projects and flavors - print "Currently attached projects (and flavors):\n" ; - foreach $pair ( @projlist ) { - @pairlist = split( /:/, $pair ) ; - ( $pairtmp = $pairlist[0] ) =~ tr/A-Z/a-z/ ; - print " $pairtmp ($pairlist[1])\n" ; - } -} elsif (( $#ARGV == 0 ) && !($ARGV[0] =~ /^\-/)) { - # list the attached flavor of the named project - foreach $pair ( @projlist ) { - @pairlist = split( /:/, $pair ) ; - ( $pairtmp = $pairlist[0] ) =~ tr/A-Z/a-z/ ; - if ( $pairtmp eq $ARGV[0] ) { - print "$pairlist[1]\n" ; - } - } -} elsif (( $#ARGV == 1 ) && ( $ARGV[0] eq "-" )){ - # list all attached projects who are attached with a given flavor - foreach $pair ( @projlist ) { - @pairlist = split( /:/, $pair ) ; - if ( $pairlist[1] eq $ARGV[1] ) { - $pairlist[0] =~ tr/A-Z/a-z/ ; - print "$pairlist[0]\n" ; - } - } -} else { - print "Usage: ctquery [project] -or-\n" ; - print " ctquery - flavor\n" ; - exit ; -} diff --git a/dtool/src/attach/ctquery.pl b/dtool/src/attach/ctquery.pl deleted file mode 100644 index d93d42e9c7..0000000000 --- a/dtool/src/attach/ctquery.pl +++ /dev/null @@ -1,37 +0,0 @@ -# return the attached flavor of given project (or empty string) -sub CTQueryProj { - local( $projs ) = $ENV{"CTPROJS"} ; - local( @projlist ) ; - @projlist = split( / +/, $projs ) ; - local( $pair ) ; - local( @pairlist ) ; - local( $ret ) = "" ; - foreach $pair ( @projlist ) { - @pairlist = split( /:/, $pair ) ; - $pairlist[0] =~ tr/A-Z/a-z/ ; - if ( $pairlist[0] eq $_[0] ) { - $ret = $pairlist[1] ; - } - } - $ret ; -} - -# return all projects attached with a given flavor -sub CTQueryFlav { - local( $projs ) = $ENV{"CTPROJS"} ; - local( @projlist ) ; - @projlist = split( / +/, $projs ) ; - local( $pair ) ; - local( @pairlist ) ; - local( $ret ) = "" ; - foreach $pair ( @projlist ) { - @pairlist = split( /:/, $pair ) ; - if ( $pairlist[1] eq $_[0] ) { - $pairlist[0] =~ tr/A-Z/a-z/ ; - $ret = $ret . " $pairlist[0]" ; - } - } - $ret ; -} - -1; diff --git a/dtool/src/attach/ctrm b/dtool/src/attach/ctrm deleted file mode 100644 index 7cac8c290e..0000000000 --- a/dtool/src/attach/ctrm +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/perl - -if ( $#ARGV < 0 ) { - exit print "Usage: ctrmelem element-name [...]\n" ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "Environment not configured for CTtools" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -foreach $item ( @ARGV ) { - if ( -e $item ) { - if ( ! &CTCMRmElem( $item, $projname, $spec ) ) { - print STDERR "Could not rmname '$item'\n" ; - } - } else { - print STDERR "No such file '$item'.\n" ; - } -} diff --git a/dtool/src/attach/ctsanity b/dtool/src/attach/ctsanity deleted file mode 100644 index 9d658275bb..0000000000 --- a/dtool/src/attach/ctsanity +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/perl - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "Environment not configured to run CTtools" ; -} - -sub CTSanityUsage { - print STDERR "Usage: ctsanity [-v]\n" ; - print STDERR "Options:\n" ; - print STDERR " -v : sanity check the .vspec files \n" ; - exit ; -} - -if ( $#ARGV == -1 ) { - &CTSanityUsage ; -} - -$check_vspecs = 0 ; - -foreach $item ( @ARGV ) { - if ( $item eq "-v" ) { - $check_vspecs = 1 ; - } else { - print STDERR "unknown option '" . $item . "'\n" ; - $CTSanityUsage ; - } -} - -require "$tool/built/include/ctvspec.pl" ; - -if ( $check_vspecs ) { - local( $projs ) = &CTListAllProjects ; - local( @projlist ) = split( / +/, $projs ) ; - local( $item ) ; - foreach $item ( @projlist ) { - print STDERR "checking " . $item . ".vspec:\n" ; - local( $ctsavedebug ) = $ctdebug ; - $ctdebug = 1 ; - &CTReadVSpec( $item ) ; - $ctdebug = $ctsavedebug ; - } -} diff --git a/dtool/src/attach/cttimewarp b/dtool/src/attach/cttimewarp deleted file mode 100755 index e64d121e61..0000000000 --- a/dtool/src/attach/cttimewarp +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/perl - -if ($#ARGV < 0) { - exit print "Usage: cttimewarp [-clear] label [time]\n" ; -} - -@arglist = @ARGV ; - -$clear = 0 ; - -if ( $arglist[0] =~ /^-c/ ) { - $clear = 1 ; - shift( @arglist ) ; -} - -if ( @arglist == () ) { - if ( $clear ) { - exit print "Usage: cttimewarp -clear label\n" ; - } else { - exit print "Usage: cttimewarp label time\n" ; - } -} - -$label = $arglist[0] ; -shift( @arglist ) ; - -if (( ! $clear ) && ( @arglist == () )) { - exit print "Usage: cttimewarp label time\n" ; -} - -$time = $arglist[0] ; - -if ( $clear ) { - $cmd = "cleartool find . -version \"lbtype(" . $label . - ")\" -exec 'cleartool rmlabel -c \"untimewarping\" " . $label . - ' $CLEARCASE_XPN' . "'\n" ; - system( $cmd ) ; -} else { - $cmd = "cleartool mklabel -replace -recurse -c \"rolling time back to " . - $time . "\" -version /main/'{\!created_since(" . $time . ")}' " . - $label . " .\n" ; - system( $cmd ) ; -} diff --git a/dtool/src/attach/ctunattach.drv b/dtool/src/attach/ctunattach.drv deleted file mode 100644 index daea40d5eb..0000000000 --- a/dtool/src/attach/ctunattach.drv +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/perl - -# acceptable forms: -# ctunattach project - attach to the personal flavor of the project - -sub CTUnattachUsage { - print STDERR "Usage: ctattach project(s)\n" ; - &CTAttachWriteNullScript( $tmpname ) ; - print $tmpname . "\n" ; - exit; -} - -$tool = $ENV{"DTOOL"} ; - -require "$tool/built/include/ctattch.pl" ; -require "$tool/built/include/ctunattach.pl" ; -require "$tool/built/include/ctquery.pl" ; - -$tmpname = "/tmp/script.$$" ; - -if ( $#ARGV == -1 ) { - &CTUnattachUsage ; -} - -foreach $proj ( @ARGV ) { - &CTUDebug( "project is '$proj'\n" ) ; - - $curflav = &CTQueryProj( $proj ) ; - if ( $curflav ne "" ) { - $envsep{"PATH"} = ":" ; - $envsep{"LD_LIBRARY_PATH"} = ":" ; - $envsep{"DYLD_LIBRARY_PATH"} = ":" ; - $envsep{"PFPATH"} = ":" ; - $envsep{"SSPATH"} = ":" ; - $envsep{"STKPATH"} = ":" ; - $envsep{"DC_PATH"} = ":" ; - $spec = &CTUnattachCompute( $proj, $curflav ) ; - if ( $spec eq "" ) { - &CTAttachWriteNullScript( $tmpname ) ; - print $tmpname . "\n" ; - } else { - &CTUnattachWriteScript( $tmpname ) ; - print $tmpname . "\n" ; - } - } else { - &CTAttachWriteNullScript( $tmpname ) ; - print $tmpname . "\n" ; - } -} diff --git a/dtool/src/attach/ctunattach.pl b/dtool/src/attach/ctunattach.pl deleted file mode 100644 index 08e867921f..0000000000 --- a/dtool/src/attach/ctunattach.pl +++ /dev/null @@ -1,251 +0,0 @@ -require "$tool/built/include/ctquery.pl" ; - -$shell_type = "csh" ; -if ( $ENV{"SHELL_TYPE"} ne "" ) { - if ( $ENV{"SHELL_TYPE"} eq "sh" ) { - $shell_type = "sh" ; - } -} - -# remove a value from a variable. If it is the only thing remaining in the -# variable, add it to the unset list. -# input is in: -# $_[0] = variable -# $_[1] = value -# -# output is in: -# %newenv = an image of how we want the environment to be -# @unset = a list of variables to unset -sub CTUnattachMod { - &CTUDebug( "in CTUnattachMod\n" ) ; - local( $done ) = 0 ; - # if we didn't get any data, nothing really to do - if ( $_[0] eq "" ) { $done = 1 ; } - if ( $_[1] eq "" ) { $done = 1 ; } - # if the variable is already set to be unset, nothing really to do - if ( join( " ", @unset ) =~ /$_[0]/ ) { $done = 1 ; } - # if the variable isn't in newenv, move it there, if it's empty mark it - # for unsetting - if ( $newenv{$_[0]} eq "" ) { - $newenv{$_[0]} = &CTSpoolEnv( $_[0] ) ; - if ( $newenv{$_[0]} eq "" ) { - push( @unset, $_[0] ) ; - delete $newenv{$_[0]} ; - $done = 1 ; - } - } - # if the value does not appear in the variable, nothing really to do - if ( ! ( $newenv{$_[0]} =~ /$_[1]/ ) ) { $done = 1 ; } - # now down to the real work - if ( ! $done ) { - # if the variable is exactly the value, mark it for unsetting - if ( $newenv{$_[0]} eq $_[1] ) { - push( @unset, $_[0] ) ; - delete $newenv{$_[0]} ; - } elsif ( $newenv{$_[0]} =~ / $_[1]/ ) { - local( $tmp ) = $newenv{$_[0]} ; - $tmp =~ s/ $_[1]// ; - $newenv{$_[0]} = $tmp ; - } elsif ( $newenv{$_[0]} =~ /$_[1] / ) { - local( $tmp ) = $newenv{$_[0]} ; - $tmp =~ s/$_[1] // ; - $newenv{$_[0]} = $tmp ; - } else { - print STDERR "ERROR: variable '" . $_[0] . "' contains '" . - $_[1] . "' (in '" . $newenv{$_[0]} . - "'), but I am too stupid to figure out how to remove it.\n" ; - } - } -} - -# given the project and flavor, build the lists of variables to set/modify -# input is in: -# $_[0] = project -# $_[1] = flavor -# -# output is in: -# return value is config line -# %newenv = an image of what we want the environment to look like -# @unset = list of variables to be unset -# %envsep = seperator -# %envcmd = set or setenv -# %envpostpend = flag that variable should be postpended -sub CTUnattachCompute { - &CTUDebug( "in CTUnattachCompute\n" ) ; - local( $flav ) = $_[1] ; - local( $spec ) = &CTResolveSpec( $_[0], $flav ) ; - local( $root ) = &CTComputeRoot( $_[0], $flav, $spec ) ; - - if ( $spec ne "" ) { - local( $proj ) = $_[0] ; - $proj =~ tr/a-z/A-Z/ ; - local( $item ) ; - - # since we don't have to worry about sub-attaches, it doesn't matter - # if we scan the .init file first or not. So we won't. - &CTUDebug( "extending paths\n" ) ; - - $item = $root . "/built/bin" ; - &CTUnattachMod( "PATH", $item ) ; - $item = $root . "/built/lib" ; - if ( $ENV{"PENV"} eq "WIN32" ) { - &CTUnattachMod( "PATH", $item ) ; - } - &CTUnattachMod( "LD_LIBRARY_PATH", $item ) ; - &CTUnattachMod( "DYLD_LIBRARY_PATH", $item ) ; - #$item = $root . "/src/all" ; - #&CTUnattachMod( "CDPATH", $item ) ; - $item = $root . "/built/include" ; - &CTUnattachMod( "CT_INCLUDE_PATH", $item ) ; - $item = $root . "/built/etc" ; - &CTUnattachMod( "ETC_PATH", $item ) ; - $item = $proj . ":" . $flav ; - &CTUnattachMod( "CTPROJS", $item ) ; - push( @unset, $proj ) ; - - if ( -e "$root/built/etc/$_[0].init" ) { - &CTUDebug( "scanning $_[0].init file\n" ) ; - local( @linesplit ) ; - local( $linetmp ) ; - local( $loop ); - local( *INITFILE ) ; - if ( -x "$root/built/etc/$_[0].init" ) { - open( INITFILE, "$root/built/etc/$_[0].init $_[0] $_[1] $root |" ) ; - } else { - open( INITFILE, "< $root/built/etc/$_[0].init" ) ; - } - while ( ) { - s/\n$// ; - @linesplit = split( /\#/ ) ; - $_ = $linesplit[0] ; - if ( $_ =~ /^MODABS/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - foreach $loop ( @linesplit ) { - &CTUnattachMod( $linetmp, $loop ) ; - } - } elsif ( $_ =~ /^MODREL/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - shift( @linesplit ) ; - shift( @linesplit ) ; - foreach $loop ( @linesplit ) { - &CTUnattachMod( $linetmp, $root . "/" . $loop ) ; - } - } elsif ( $_ =~ /^SETABS/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - push( @unset, $linetmp ) ; - } elsif ( $_ =~ /^SETREL/ ) { - @linesplit = split ; - $linetmp = $linesplit[1] ; - push( @unset, $linetmp ) ; - } elsif ( $_ =~ /^SEP/ ) { - @linesplit = split ; - $envsep{$linesplit[1]} = $linesplit[2] ; - } elsif ( $_ =~ /^CMD/ ) { - @linesplit = split ; - $envcmd{$linesplit[1]} = $linesplit[2] ; - } elsif ( $_ =~ /^DOCSH/ ) { - &CTUDebug( "ignoring DO command in .init file\n" ) ; - } elsif ( $_ =~ /^DOSH/ ) { - &CTUDebug( "ignoring DO command in .init file\n" ) ; - } elsif ( $_ =~ /^DO/ ) { - &CTUDebug( "ignoring DO command in .init file\n" ) ; - } elsif ( $_ =~ /^POSTPEND/ ) { - @linesplit = split ; - $envpospend{$linesplit[1]} = 1 ; - } elsif ( $_ =~ /^ATTACH/ ) { - &CTUDebug( "ignoring ATTACH command in .init file\n" ) ; - } else { - print STDERR "Unknown .init directive '$_'\n" ; - } - } - close( INITFILE ) ; - } - } - &CTUDebug( "out of CTUnattachCompute\n" ) ; - $spec ; -} - -# write a script to setup the environment -# Input is: -# $_[0] = filename -sub CTUnattachWriteScript { - &CTUDebug( "in CTAttachWriteScript\n" ) ; - local( *OUTFILE ) ; - open( OUTFILE, ">$_[0]" ) ; - print OUTFILE "#!/bin/" . $shell_type . " -f\n" ; - local( $item ) ; - #local( $unsetcdpath ) = 0 ; - #local( $modcdpath ) = 0 ; - - foreach $item ( @unset ) { - #if ( $item eq "CDPATH" ) { $unsetcdpath = 1 ; } - - if ( $shell_type eq "sh" ) { - print OUTFILE "$item=\n" ; - if ( $envcmd{$item} ne "set" ) { - print OUTFILE "export $item\n" ; - } - } else { - if ( $envcmd{$item} ne "" ) { - print OUTFILE "un" . $envcmd{$item} . " $item\n" ; - } else { - print OUTFILE "unsetenv $item\n" ; - } - } - } - foreach $item ( keys %newenv ) { - #if ( $item eq "CDPATH" ) { $modcdpath = 1 ; } - - local( $sep ) = " " ; - if ( $envsep{$item} ne "" ) { - $sep = $envsep{$item} ; - } - local( @splitlist ) = split( / +/, $newenv{$item} ) ; - local( $outval ) = join( $sep, @splitlist ) ; - - if ( $shell_type eq "sh" ) { - print OUTFILE "$item=\"" . $outval . "\"\n" ; - if ( $envcmd{$item} ne "set" ) { - print OUTFILE "export $item\n" ; - } - } else { - if ( $envcmd{$item} ne "" ) { - PRINT OUTFILE $envcmd{$item} . " $item " ; - if ( $envcmd{$item} eq "set" ) { - print OUTFILE " = ( " ; - } - print OUTFILE $outval ; - if ( $envcmd{$item} eq "set" ) { - print OUTFILE ")" ; - } - print OUTFILE "\n" ; - } else { - print OUTFILE "setenv $item \"$outval\"\n" ; - } - } - } - #if ( $unsetcdpath ) { - # if ( $shell_type ne "sh" ) { - # print OUTFILE "unset cdpath\n" ; - # } - #} elsif ( $modcdpath ) { - # if ( $shell_type ne "sh" ) { - # print OUTFILE "set cdpath = ( \$" . "CDPATH )\n" ; - # } - #} - - if (! $ctdebug) { - print OUTFILE "rm -f $_[0]\n" ; - } else { - print STDERR "no self-destruct script '" . $_[0] . "'\n" ; - } - close( OUTFILE ) ; - &CTUDebug( "out of CTUnattachWriteScript\n" ) ; -} - -1; diff --git a/dtool/src/attach/ctunco b/dtool/src/attach/ctunco deleted file mode 100644 index d96eab41dc..0000000000 --- a/dtool/src/attach/ctunco +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/perl - -if ( $#ARGV < 0 ) { - exit print "Usage ctunco element-name [...]\n" ; -} - -$tool = $ENV{"DTOOL"} ; -if ( $tool eq "" ) { - die "not configured for using CTtools" ; -} - -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctvspec.pl" ; -require "$tool/built/include/ctquery.pl" ; -require "$tool/built/include/ctproj.pl" ; -require "$tool/built/include/ctcm.pl" ; - -$projname = &CTProj ; -$projname =~ tr/A-Z/a-z/ ; -$flav = &CTQueryProj( $projname ) ; -$spec = &CTResolveSpec( $projname, $flav ) ; - -foreach $item ( @ARGV ) { - if ( -e $item ) { - if ( ! &CTCMUncheckout( $item, $projname, $spec ) ) { - print STDERR "Could not uncheckout '$item'\n" ; - } - } else { - print STDERR "No such file '$item'.\n" ; - } -} diff --git a/dtool/src/attach/ctutils.pl b/dtool/src/attach/ctutils.pl deleted file mode 100644 index a32dd5a6f3..0000000000 --- a/dtool/src/attach/ctutils.pl +++ /dev/null @@ -1,47 +0,0 @@ -# evaluate the given parameter to expand shell variables -sub CTUShellEval { - local( *CTUSEFILE ) ; - open( CTUSEFILE, "echo $_[0] |" ) ; - local( $CTUSEret ) = ; - close( CTUSEFILE ) ; - $CTUSEret =~ s/\n$// ; - $CTUSEret ; -} - -# if debug is on, print the argument -sub CTUDebug { - if ( $ctdebug ) { - print STDERR $_[0] ; - } -} - -use Cwd ; -# get current directory -sub CTUCurrDir { - local( $pwd ) = getcwd() ; - if ( $pwd =~ /^\/vobs/ ) { - local( *VFILE ) ; - open( VFILE, "cleartool pwv -short |" ) ; - local( $view ) = ; - close( VFILE ) ; - $view =~ s/\n$// ; - $pwd = "/view/" . $view . $pwd ; - } - $pwd ; -} - -# turn a shell return code into a success/fail flag -sub CTURetCode { - local( $ret ) ; - if ( $_[0] == 0 ) { - $ret = 1 ; - } else { - $ret = 0 ; - } - $ret ; -} - -$ctdebug = $ENV{"CTATTACH_DEBUG"} ; -$ctvspec_path = '/usr/local/etc' unless $ctvspec_path = $ENV{'CTVSPEC_PATH'}; - -1; diff --git a/dtool/src/attach/ctvspec.pl b/dtool/src/attach/ctvspec.pl deleted file mode 100644 index 8f556d2ee7..0000000000 --- a/dtool/src/attach/ctvspec.pl +++ /dev/null @@ -1,360 +0,0 @@ -require "$tool/built/include/ctutils.pl" ; - -# read a .vspec file into a map -# $_[0] = project -# on exit $ctvspecs{} will contain the data -# -# vspec format: -# tag:type:other data -# -# type: ref, root, vroot, croot -# other data: -# ref: name=_____ - required, take to refference -# root: path=_____ - required, path of tree root -# vroot: name=_____ - optional, name of view to use (if not tag) -# croot: path=_____ - required, local path of tree root -# server=_____ - required, CVS server string, ',' for ':' - -sub CTReadVSpec { - &CTUDebug( "reading vspec file for project " . $_[0] . "\n" ) ; - local( $ret ) = "" ; - local( $thisproj ) = $_[0] ; - if ( -e "$ctvspec_path/$thisproj.vspec" ) { - %ctvspecs = () ; - local( *SPECFILE ) ; - open( SPECFILE, "<$ctvspec_path/$thisproj.vspec" ) ; - local( @partlist ) ; - while ( $_ = ) { - s/\n$// ; - @partlist = split( /\#/ ) ; - $_ = $partlist[0] ; - if ( $_ ne "" ) { - @partlist = split( /:/ ); - local( $tag ) = $partlist[0] ; - shift( @partlist ) ; - local( $spec ) = join( ":", @partlist ) ; - if ( &CTValidateSpec( $spec ) ) { - $ctvspecs{$tag} = $spec ; - if ( $ctdebug ) { - print STDERR "tag(" . $tag . ") = " . $spec . "\n" ; - } - } - } - } - close( SPECFILE ) ; - $ctvspec_read = $_[0] ; - } else { - print STDERR "CTReadVSpec: cannot locate '$ctvspec_path/$thisproj.vspec'\n" ; - print STDERR "(did you forget to run the \$WINTOOLS/cp_vspec script?)\n" ; - } -} - -# given a spec line return it's type -# $_[0] = spec line - -sub CTSpecType { - local( @speclist ) = split( /:/, $_[0] ) ; - $speclist[0] ; -} - -# given a spec line return it's options if any -# $_[0] = spec line - -sub CTSpecOptions { - local( @speclist ) = split( /:/, $_[0] ) ; - shift( @speclist ) ; - join( ":", @speclist ) ; -} - -# given the options part of a spec line, find a given option -# $_[0] = options line -# $_[1] = desired option - -sub CTSpecFindOption { - local( $ret ) = "" ; - local( @options ) = split( /:/, $_[0] ) ; - local( $item ) ; - local( @itemlist ) ; - foreach $item ( @options ) { - @itemlist = split( /=/, $item ) ; - if ( $itemlist[0] eq $_[1] ) { - $ret = $itemlist[1] ; - } - } - $ret ; -} - -# resolve a final spec line for a given flavor -# $_[0] = project -# $_[1] = flavor - -sub CTResolveSpec { - &CTUDebug( "in CTResolveSpec\n" ) ; - local( $proj ) = $_[0] ; - $proj =~ tr/A-Z/a-z/ ; - if ( $ctvspec_read ne $proj ) { - &CTReadVSpec( $proj ) ; - } - local( $spec ) = $ctvspecs{$_[1]} ; - local( $ret ) = "" ; - if ( $spec ne "" ) { - local( $type ) = &CTSpecType( $spec ) ; - local( @speclist ) = split( /:/, &CTSpecOptions( $spec ) ) ; - if ( $type eq "ref" ) { - local( @optionlist ) = split( /=/, $speclist[0] ) ; - if ( $optionlist[0] ne "name" ) { - print STDERR "bad data attached to flavor " . $_[1] . - " of project " . $proj . "\n" ; - } else { - local( $tmp ) = &CTUShellEval( $optionlist[1] ) ; - if ( $ctdebug ) { - print STDERR "resolved a 'ref' to " . $tmp . - ", recuring\n" ; - } - $ret = &CTResolveSpec( $proj, $tmp ) ; - } - } else { - $ret = $spec ; - } - } - if ( $ret eq "" ) { - print STDERR "unknown flavor " . $_[1] . " of project " . $proj . - "\n" ; - } - &CTUDebug( "out of CTResolveSpec\n" ) ; - $ret ; -} - -# resolve the final name for a given flavor -# $_[0] = project -# $_[1] = flavor - -sub CTResolveSpecName { - &CTUDebug( "in CTResolveSpecName\n" ) ; - local( $proj ) = $_[0] ; - $proj =~ tr/A-Z/a-z/ ; - if ( $ctvspec_read ne $proj ) { - &CTReadVSpec( $proj ) ; - } - local( $spec ) = $ctvspecs{$_[1]} ; - local( $ret ) = $_[1] ; - if ( $spec ne "" ) { - local( $type ) = &CTSpecType( $spec ) ; - local( @speclist ) = split( /:/, &CTSpecOptions( $spec ) ) ; - if ( $type eq "ref" ) { - local( @optionlist ) = split( /=/, $speclist[0] ) ; - if ( $optionlist[0] ne "name" ) { - print STDERR "bad data attached to flavor " . $_[1] . - " of project " . $proj . "\n" ; - } else { - local( $tmp ) = &CTUShellEval( $optionlist[1] ) ; - if ( $ctdebug ) { - print STDERR "resolved a 'ref' to " . $tmp . - ", recuring\n" ; - } - $ret = &CTResolveSpecName( $proj, $tmp ) ; - } - } - } - if ( $ret eq "" ) { - print STDERR "unknown flavor " . $_[1] . " of project " . $proj . - "\n" ; - } - &CTUDebug( "out of CTResolveSpecName\n" ) ; - $ret ; -} - -# validate a spec line -# $_[0] = spec line - -sub CTValidateSpec { - local( $ret ) = 0 ; - local( $type ) = &CTSpecType( $_[0] ) ; - local( @speclist ) = split( /:/, &CTSpecOptions( $_[0] ) ) ; - local( $have_error ) = 0 ; - local( $item ) ; - local( @itemlist ) ; - if ( $type eq "ref" ) { - local( $have_name ) = 0 ; - foreach $item ( @speclist ) { - @itemlist = split( /=/, $item ) ; - if ( $itemlist[0] eq "name" ) { - if ( $have_name ) { - $have_error = 1; - &CTUDebug( "multiple name options on 'ref'\n" ) ; - } - $have_name = 1; - } else { - &CTUDebug( "invalid option on 'ref' = " . $item . "\n" ) ; - $have_error = 1 ; - } - } - if ( ! $have_error ) { - if ( $have_name ) { - $ret = 1 ; - } - } - } elsif ( $type eq "root" ) { - local( $have_path ) = 0 ; - foreach $item ( @speclist ) { - @itemlist = split( /=/, $item ) ; - if ( $itemlist[0] eq "path" ) { - if ( $have_path ) { - $have_error = 1 ; - &CTUDebug( "multiple path options on 'root'\n" ) ; - } - $have_path = 1 ; - } else { - &CTUDebug( "invalid option on 'root' = " . $item . "\n" ) ; - $have_error = 1 ; - } - } - if ( ! $have_error ) { - if ( $have_path ) { - $ret = 1 ; - } - } - } elsif ( $type eq "vroot" ) { - local( $have_name ) = 0 ; - foreach $item ( @speclist ) { - @itemlist = split( /=/, $item ) ; - if ( $itemlist[0] eq "name" ) { - if ( $have_name ) { - $have_error = 1 ; - &CTUDebug( "multiple name options on 'vroot'\n" ) ; - } - $have_name = 1 ; - } else { - &CTUDebug( "invalid option on 'vroot' = " . $item . "\n" ) ; - $have_error = 1 ; - } - } - if ( ! $have_error ) { - $ret = 1 ; - } - } elsif ( $type eq "croot" ) { - local( $have_path ) = 0 ; - local( $have_server ) = 0 ; - foreach $item ( @speclist ) { - @itemlist = split( /=/, $item ) ; - if ( $itemlist[0] eq "path" ) { - if ( $have_path ) { - $have_error = 1 ; - &CTUDebug( "multiple path options on 'croot'\n" ) ; - } - $have_path = 1 ; - } elsif ( $itemlist[0] eq "server" ) { - if ( $have_server ) { - $have_error = 1 ; - &CTUDebug( "multiple server options on 'croot'\n" ) ; - } - $have_server = 1 ; - } else { - &CTUDebug( "invalid option on 'croot' = " . $item . "\n" ) ; - $have_error = 1 ; - } - } - if ( ! $have_error ) { - if ( $have_path && $have_server ) { - $ret = 1 ; - } - } - } else { - &CTUDebug( "unknow spec type '" . $speclist[0] . "'\n" ) ; - } - $ret ; -} - -# get a list of all projects - -sub CTListAllProjects { - &CTUDebug( "in CTListAllProjects\n" ) ; - local( $ret ) = "" ; - local( $done ) = 0 ; - local( *DIRFILES ) ; - open( DIRFILES, "(cd $ctvspec_path ; /bin/ls -1 *.vspec ; echo blahblah) |" ) ; - while ( ! $done ) { - $_ = ; - s/\n$// ; - if ( $_ eq "blahblah" ) { - $done = 1 ; - } else { - s/.vspec$// ; - if ( $_ ne "" ) { - if ( $ret eq "" ) { - $ret = $_ ; - } else { - $ret = $ret . " " . $_ ; - } - } - } - } - close( DIRFILES ) ; - &CTUDebug( "final list of projects '" . $ret . "'\n" . - "out of CTListAllProjects\n" ) ; - $ret ; -} - -# list all flavors of a project -# $_[0] = project - -sub CTListAllFlavors { - &CTUDebug( "in CTListAllFlavors\n" ) ; - local( $proj ) = $_[0] ; - $proj =~ tr/A-Z/a-z/ ; - if ( $ctvspec_read ne $proj ) { - &CTReadVSpec( $proj ) ; - } - local( $ret ) = ""; - local( $item ) ; - foreach $item ( keys %ctvspecs ) { - if ( $ret eq "" ) { - $ret = $item ; - } else { - $ret = $ret . " " . $item ; - } - } - &CTUDebug( "out of CTListAllFlavors\n" ) ; - $ret ; -} - -# given a project and a spec, determine the local root of the project -# $_[0] = project -# $_[1] = flavor -# $_[2] = spec line - -sub CTComputeRoot { - &CTUDebug( "in CTComputeRoot\n" ) ; - local( $proj ) = $_[0] ; - $proj =~ tr/A-Z/a-z/ ; - if ( $ctvspec_read ne $proj ) { - &CTReadVSpec( $proj ) ; - } - local( $ret ) = "" ; - local( $type ) = &CTSpecType( $_[2] ) ; - local( $options ) = &CTSpecOptions( $_[2] ) ; - local( $vname ) = &CTResolveSpecName( $proj, $_[1] ) ; - &CTUDebug( "type = '" . $type . "' with options '" . $options . "'\n" ) ; - if ( $type eq "root" ) { - $ret = &CTSpecFindOption( $options, "path" ) ; - } elsif ( $type eq "vroot" ) { - local( $name ) = &CTSpecFindOption( $options, "name" ) ; - if ( $name ne "" ) { - $ret = "/view/$name/vobs/$proj" ; - } else { - $ret = "/view/$vname/vobs/$proj" ; - } - } elsif ( $type eq "croot" ) { - $ret = &CTSpecFindOption( $options, "path" ) ; - } elsif ( $ctdebug) { - print STDERR "unknown flavor type '" . $type . "'\n" ; - } - &CTUDebug( "returning '" . $ret . "'\n" ) ; - &CTUDebug( "out of CTComputeRoot\n" ) ; - $ret ; -} - -%ctvspecs = () ; -$ctvspec_read = "" ; - -1; diff --git a/dtool/src/attach/dtool.alias b/dtool/src/attach/dtool.alias deleted file mode 100644 index eaebd79b6f..0000000000 --- a/dtool/src/attach/dtool.alias +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/csh -f - -setenv OS `uname` -setenv USER `whoami` - -if ( -e $DTOOL/bin/neartool ) setenv HAVE_NEARTOOL "yes" -if ( ! $?HAVE_NEARTOOL ) setenv HAVE_NEARTOOL "no" -if ( ! $?HAVE_ATRIA ) setenv HAVE_ATRIA "no" - -set host=$HOST -if ( ! $?TERM ) setenv TERM "none" -if ( $TERM == "iris-ansi" || $TERM == "iris-ansi-net" ) then - alias ctshowprojs 'echo -n "\033P1.y"$USER"@"$host" -- "$CTPROJS"\033\\"; echo -n "\033P3.y"`echo $CTPROJS | cut -f1 -d:`\($host\)"\033\\"' -else if ( $TERM == "xterm" || $TERM == "color-xterm" || $TERM == "cygwin" ) then - alias ctshowprojs 'echo -n "\033]2;"$USER"@"$host" -- "$CTPROJS"\007"; echo -n "\033]1;"`echo $CTPROJS | cut -f1 -d:`\($host\)"\007"' -else - alias ctshowprojs 'echo $CTPROJS' -endif - -alias ctattach 'source `ctattach.drv \!*`; ctshowprojs' -alias cta 'ctattach' -alias cta-ship 'setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV ship ; ctattach \!* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' -alias cta-release 'setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV release ; ctattach \!* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' -alias cta-install 'setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV install ; ctattach \!* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' -alias ctunattach 'source `ctunattach.drv \!*`; ctshowprojs' -alias ctuna 'ctunattach' - -#Modifications to emacs alias by Jason -#To allow for NTEmacs to run like emacs on unix boxes -if (($OS == "CYGWIN_NT-4.0") || ($OS == "CYGWIN_NT-5.0" ) || ($OS == "CYGWIN_NT-5.1" )) then - alias emacs 'emacs -T "$USER@$HOST $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`($HOST)" -bg #002040 -fg #00C0FF -cr yellow -ms yellow -l `cygpath -w ~/.emacs` $CTEMACS_OPTS' -else - alias emacs 'emacs -T "$USER@$HOST $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`($HOST)" $CTEMACS_OPTS' -endif - -alias rlogin 'rlogin \!*; ctshowprojs' -alias telnet 'telnet \!*; ctshowprojs' diff --git a/dtool/src/attach/dtool.alias-sh b/dtool/src/attach/dtool.alias-sh deleted file mode 100644 index 54dddf782b..0000000000 --- a/dtool/src/attach/dtool.alias-sh +++ /dev/null @@ -1,46 +0,0 @@ -#! /bin/sh - -if [ -e $DTOOL/bin/neartool ]; then HAVE_NEARTOOL="yes"; fi -if [ -z "$HAVE_NEARTOOL" ]; then HAVE_NEARTOOL="no"; fi -export HAVE_NEARTOOL - -if [ -z "$HAVE_ATRIA" ]; then HAVE_ATRIA="no"; fi -export HAVE_ATRIA - -if [ $HAVE_NEARTOOL = "yes" ]; then - alias ct='neartool' -else - alias ct='cleartool' -fi - -alias ctshowprojs='echo -n "\033P1.y"$USER"@"$HOST" -- "$CTPROJS"\033\\"; echo -n "\033P3.y"`echo $CTPROJS | cut -f1 -d:`\(`uname -n`\)"\033\\"' -alias ctattach='source `ctattach.drv $*`; ctshowprojs' -alias cta='ctattach' -alias cta-ship='setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV ship ; ctattach $* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' -alias cta-release='setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV release ; ctattach $* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' -alias cta-install='setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV install ; ctattach $* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' -alias ctunattach='source `ctunattach.drv $*`; ctshowprojs' -alias ctuna='ctunattach' - -if [ "$PENV" = "WIN32_DREAMCAST" ]; then - alias ctci='neartool ci' - alias ctco='neartool co' - alias ctmake='gmake' - alias emacs='/emacs/bin/runemacs' - alias emacs='/emacs/bin/runemacs -T "`logname`@`uname -n` $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`(`uname -n`)" $CTEMACS_OPTS' - -elif [ "$HAVE_NEARTOOL" = "yes" ]; then - alias ctci='neartool ci' - alias ctco='neartool co' - alias ctmake='make' - alias emacs='emacs -T "`logname`@`uname -n` $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`(`uname -n`)" $CTEMACS_OPTS' - -elif [ "$HAVE_ATRIA" = "yes" ]; then - alias ctci='cleartool ci' - alias ctco='cleartool co' - alias ctmake 'clearmake -C gnu $* |& grep -v "^clearmake: Warning: Config"' - alias emacs='emacs -T "`logname`@`uname -n` $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`(`uname -n`)" $CTEMACS_OPTS' -fi - -alias rlogin='rlogin $*; ctshowprojs' -alias telnet='telnet $*; ctshowprojs' diff --git a/dtool/src/attach/dtool.cshrc b/dtool/src/attach/dtool.cshrc deleted file mode 100644 index 948f190625..0000000000 --- a/dtool/src/attach/dtool.cshrc +++ /dev/null @@ -1,99 +0,0 @@ -#!/bin/csh -f - -setenv OS `uname` - -# careful, security exploit here -setenv LD_LIBRARY_PATH "." -setenv DYLD_LIBRARY_PATH "." - -setenv CTEMACS_FOREHIGHLIGHT white -setenv CTEMACS_BACKHIGHLIGHT blue - -# Setup the initial path -if ( $OS == "Linux" ) then - set path = ( /bin /bin /usr/bin /sbin /usr/sbin /usr/bin/X11 \ - /usr/etc /usr/local/bin /var/local/bin ~/bin ) -else if ( $OS == "Darwin" ) then - set path = ( /bin /usr/bin /sbin /usr/sbin /usr/local/bin ~/bin $path ) -else if ( $OS == "IRIX64" ) then - set path = ( /var/local/bin ~/bin /usr/local/prman/bin \ - /usr/sbin /usr/bsd /sbin /usr/bin /bin /usr/bin/X11 /usr/etc \ - /usr/demos/bin /usr/local/bin ) - setenv LD_LIBRARY_PATH "/usr/local/lib:." -else if (($OS == "CYGWIN_NT-6.1-WOW64") || ($OS == "CYGWIN_NT-6.1") || ($OS == "CYGWIN_NT-5.1") || ($OS == "CYGWIN_NT-6.0") || ($OS == "CYGWIN_NT-6.0-WOW64") || ($OS == "CYGWIN_NT-5.2-WOW64") || ($OS == "CYGWIN_NT-5.0") || ( $OS == "CYGWIN_NT-4.0" ) || ( $OS == "WINNT" )) then - set path = ( /bin /usr/bin /usr/lib /usr/local/bin $path . ) - if ( $?LIB ) then - setenv LIB "$LIB;"`cygpath -w /usr/lib` - else - setenv LIB `cygpath -w /usr/lib` - endif -else if (( $OS == "CYGWIN_98-4.10" ) || ( $OS == "WIN95" )) then - set path = ( /bin /usr/local/bin /contrib/bin /msvc98/Bin \ - /mscommon/MSDev98/Bin /mscommon/Tools /usr/lib $path ) - setenv LIB `cygpath -w /msvc98/mfc/lib`\;`cygpath -w /msvc98/lib`\;`cygpath -w /usr/lib` - setenv INCLUDE `cygpath -w /msvc98/Include` -else - set path = ( /var/local/bin ~/bin /usr/local/prman/bin \ - /usr/sbin /usr/bsd /sbin /usr/bin /bin /usr/bin/X11 /usr/etc \ - /usr/demos/bin /usr/local/bin ) -endif - -# Setup the initial manpath -#if ( $OS == "Linux" ) then -#setenv MANPATH "/usr/local/man:/usr/man/preformat:/usr/man:/usr/X11R6/man" -#else if ( $OS == "IRIX64" ) then -#setenv MANPATH "/usr/share/catman:/usr/catman:/usr/local/share/catman:/usr/local/share/man:/usr/local/man" -#else if (( $OS == "CYGWIN_NT-5.1") || ( $OS == "CYGWIN_NT-5.2-WOW64" ) || ( $OS == "CYGWIN_NT-5.0") || ( $OS == "CYGWIN_NT-4.0" ) || ( $OS == "CYGWIN_98-4.10" ) || ( $OS == "WIN95" )) then -#setenv MANPATH "/usr/man:/contrib/man" -#else -#setenv MANPATH "/usr/share/catman:/usr/catman:/usr/local/share/catman:/usr/local/share/man:/usr/local/man" -#endif - -setenv CT_INCLUDE_PATH "." -#set cdpath = ( . ) -#setenv CDPATH "." -setenv DC_PATH "." -setenv SSPATH "." -setenv STKPATH "." - -if ( ! $?HAVE_ATRIA ) then - if ( -e /usr/atria ) then - /usr/atria/bin/cleartool mount -all >& /dev/null - if ( $status == 0 ) setenv HAVE_ATRIA "yes" - endif -endif - -if ( ! $?CTDEFAULT_FLAV ) setenv CTDEFAULT_FLAV "default" -if ( ! $?CTEMACS_OPTS ) setenv CTEMACS_OPTS "" - -if ( -e /usr/atria/bin ) set path = ( /usr/atria/bin $path ) -rehash - -if ( ! $?PENV ) then - if ( $OS == "Linux" ) then - setenv PENV "Linux" - else if ( $OS == "IRIX64" ) then - setenv PENV "SGI" - else if (( $OS == "CYGWIN_NT-5.1") || ( $OS == "CYGWIN_NT-5.0") || ( $OS == "CYGWIN_NT-6.0")|| ( $OS == "CYGWIN_NT-4.0" ) || ( $OS == "CYGWIN_98-4.10" ) || ( $OS == "WIN95" )) then - setenv PENV "WIN32" - else - setenv PENV "SGI" - endif -endif - -if ( ! $?DTOOL ) setenv DTOOL /beta/player/bootstrap/dtool -if ( $#argv == 0 ) then - setenv SETUP_SCRIPT `$DTOOL/built/bin/ctattach.drv dtool default` -else - setenv SETUP_SCRIPT `$DTOOL/built/bin/ctattach.drv dtool $argv[1]` -endif - -if($SETUP_SCRIPT == "") then - echo "error: ctattach.drv returned NULL string for setup_script filename!" - echo " 'dtool/built/bin/ctattach.drv' probably doesnt exist, need to make install on dtool to copy it from dtool\src\attach" - exit -endif - -source $SETUP_SCRIPT - - diff --git a/dtool/src/attach/dtool.emacs b/dtool/src/attach/dtool.emacs deleted file mode 100644 index dae0bde1cc..0000000000 --- a/dtool/src/attach/dtool.emacs +++ /dev/null @@ -1,295 +0,0 @@ -;; make the mouse pointer avoid the text point -;; Actually, everyone really hates this. -;(cond (window-system -; (require 'avoid) -; (mouse-avoidance-mode 'cat-and-mouse))) - -;; Make sure utf-8 is at the top of the coding-system list; Panda's -;; TextNode uses utf-8 encoding natively, so we may have some -;; documents and code written in utf-8. - -(prefer-coding-system 'utf-8) - - -;; make sure we have the compile library available to us -(load-library "compile") -;; Comment given for last checkout command -(setq last-co-comment "") -;; Comment given for last checkin command -(setq last-ci-comment "") -;; Target given for the last local make -(setq last-lm-target "realinstall") -;; Target given for the last global make -(setq last-gm-target "install") -;; Host given for the last ctrelease -(setq last-rel-host "") -;; Host given for the last ctship -(setq last-ship-host "") - -;; check the environment -(setq ct-tool (getenv "DTOOL")) -(setq have-atria (let ((h-a (getenv "HAVE_ATRIA"))) - (if (string= h-a "yes") t '()))) -;; (setq have-neartool (let ((h-n (getenv "HAVE_NEARTOOL"))) -;; (if (string= h-n "yes") t '()))) -(setq is-cygwin (or (string= (getenv "OS") "CYGWIN_NT-4.0") - (string= (getenv "OS") "CYGWIN_NT-5.0") - (string= (getenv "OS") "CYGWIN_NT-5.1"))) - -;; (setq ct-command (cond -;; (is-cygwin "bash /install/tool/bin/neartool") -;; (have-atria "cleartool") -;; (have-neartool "neartool") -;; t nil)) - -;; Load the Hightlight coloring scheme -(if is-cygwin -;; (let ((filename (concat (getenv "CYGWIN_ROOT") "install\\tool\\etc\\color.emacs"))) - (let ((filename (concat (getenv "CYGWIN_ROOT") ct-tool "\\etc\\color.emacs"))) - (if (file-readable-p filename) (load filename)))) - -;; Checkout element in the current buffer -(defun ct-checkout-curr (comment) - "Checkout version in current buffer with COMMENT." - (interactive (list (read-string "Comment: " last-co-comment))) - (setq last-co-comment comment) - (setq pname (file-name-nondirectory (buffer-file-name))) - (ct-shell-command-verbose - (concat "ctco -c " (ct-quote-string comment) " " pname)) - (ct-find-curr-file-again nil) -) - -;; Uncheckout element in the current buffer -(defun ct-uncheckout-curr () - "Uncheckout version in current buffer and remove private data." - (interactive) - (if (y-or-n-p "Ok to un-checkout? ") - (progn - (setq pname (file-name-nondirectory (buffer-file-name))) - (ct-shell-command-verbose (concat "ctunco " pname)) - (ct-find-curr-file-again t) - ) - (progn - (message "Uncheckout canceled.") - ) - ) -) - -;; Checkin element in the current buffer -(defun ct-checkin-curr () - "Checkin version in current buffer." - (interactive) - (setq pname (file-name-nondirectory (buffer-file-name))) - (setq option nil) - (while (not option) - (setq choice (read-string "Comment: s (same), n (new), l (list): " "s")) - (cond - ((equal choice "s") - (setq option "-nc")) - ((equal choice "n") - (setq comment (read-string "Comment: " last-ci-comment)) - (setq last-ci-comment comment) - (setq option (concat "-c " (ct-quote-string comment)))) - ((equal choice "l") - (ct-shell-command-verbose (concat "ctihave " pname))) - (t - (message (concat "Unrecognized choice: " choice ".")) - (sleep-for 2)))) - - (ct-shell-command-verbose (concat "ctci " option " " pname)) - (ct-find-curr-file-again t) -) - -;; Delta element in the current buffer -(defun ct-delta-curr () - "Delta element in current buffer." - (interactive) - (if (y-or-n-p "Ok to delta? ") - (progn - (setq pname (file-name-nondirectory (buffer-file-name))) - (ct-shell-command-verbose (concat "ctdelta " pname)) - (ct-find-curr-file-again t) - ) - (progn - (message "Delta canceled.") - ) - ) -) - -;; List element checkout data for the current buffer -(defun ct-lscheckout-curr () - "List checkout for the current buffer." - (interactive) - (setq pname (file-name-nondirectory (buffer-file-name))) - (ct-shell-command-verbose (concat "ctihave &")) -) - -;; List elements in the current directory that are checked out -(defun ct-lscheckout-curr-dir () - "List checkouts for the current directory." - (ct-shell-command-verbose (concat "ctihave &")) -) - -;; call clearmake in the local directory -(defun ct-local-make () - "Build TARGET from the current directory." - (interactive) - (setq target (read-string "Local build target: " last-lm-target)) - (setq last-lm-target target) - (if have-atria - (compile-internal - (concat "clearmake -C gnu " target - " |& grep -v \"clearmake: Warning: Config\"") - "No more errors.") - (compile-internal - (concat "make " target) "No more errors.")) -) - -;; call clearmake in the project root directory -(defun ct-global-make () - "Build TARGET from the project root." - (interactive) - (setq target (read-string "Global build target: " last-gm-target)) - (setq last-gm-target target) - (cond - (have-atria - (compile-internal - (concat "cd `ctproj -r` ; clearmake -C gnu " target - " |& grep -v \"clearmake: Warning: Config\"") - "No more errors.")) - (is-cygwin - (compile-internal - (concat "bash -f 'cd `ctproj -r` ; make " target "'") "No more errors.")) - (t - (compile-internal - (concat "cd `ctproj -r` ; make " target) "No more errors.")) - ) - ) - -;; Do an xdiff on the current buffer to see what is different about this -;; file from the previous version. -(defun ct-xdiff-curr () - "Show changes to element in current buffer from the previous version." - (interactive) - (setq pname (file-name-nondirectory (buffer-file-name))) - (if is-cygwin - (ct-shell-command-verbose (concat ct-command " xdiff -pre " pname)) - - ; The is a hack to deal with the fact that diff returns 1 if the - ; two files do not match. - (ct-shell-command-verbose (concat ct-command " xdiff -pre " pname ";:&"))) - ) - - -;; Make a new element for the current buffer -(defun ct-mk-elem () - (interactive) - (if (y-or-n-p (format "Make new element for %s? " - (file-name-nondirectory (buffer-name)))) - (progn - (write-file (buffer-file-name)) - (ct-shell-command-verbose - (concat "ctmkelem -eltype text_file -c '' " - (file-name-nondirectory (buffer-name)))) - ) - (progn - (message "Make element canceled.") - ) - ) - ) - -;; utility functions -(defun ct-shell-command-verbose (command) - "Execute COMMAND in shell with message." - (interactive "Shell command: \n") - (message (concat "Executing: " command " ...")) - (shell-command command) - (message "Done.") -) - -(defun ct-find-curr-file-again (read-only) - "Read in the currect file again, READONLY (t) or not (nil)." - (setq pname (buffer-file-name)) - (setq linenum (1+ (count-lines 1 (point)))) - (kill-buffer (buffer-name)) - (if read-only - (find-file-read-only pname) - (find-file pname)) - (goto-line linenum) -) - -(defun ct-quote-string (string) - "Enclose STRING in single or double quotes." - (setq has-double (string-match "\"" string)) - (setq has-single (string-match "'" string)) - (cond - ((or (and (not has-single) (not has-double)) - (and has-double (not has-single))) - (concat "'" string "'")) - ((and has-single (not has-double)) - (concat "\"" string "\"")) - (t - (message (concat "Can't quote string correctly: " string)) - (sleep-for 3) - (concat "\"" string "\""))) -) - -;; default key bindings -(global-set-key "\C-xco" 'ct-checkout-curr) -(global-set-key "\C-xcu" 'ct-uncheckout-curr) -(global-set-key "\C-xci" 'ct-checkin-curr) -(global-set-key "\C-xcd" 'ct-delta-curr) -(global-set-key "\C-xcl" 'ct-lscheckout-curr) -(global-set-key "\C-xcL" 'ct-lscheckout-curr-dir) -(global-set-key "\C-xcm" 'ct-local-make) -(global-set-key "\C-xcM" 'ct-global-make) -(global-set-key "\C-xcx" 'ct-xdiff-curr) -(global-set-key "\C-xce" 'ct-mk-elem) - -;; ok, lets make sure we load all other .emacs files we might need. This is -;; attach related code. - -(defun ct-load-project-emacs-file (proj-name) - (if (string= proj-name "DTOOL") nil - (let ((pre-name (getenv proj-name))) - (if pre-name - (let ((filename (concat pre-name "/built/etc/" - (downcase proj-name) ".emacs"))) - (if (file-readable-p filename) - (load filename)) - ))) - ) - ) - -(defun ct-break-space-colon-str (string) - (if (string= string "") - '() - (let ((substr-end (string-match ":" string 0))) - (cons (substring string 0 substr-end) - (let ((new-string-start (string-match " " string 0))) - (if (eq nil new-string-start) - '() - (ct-break-space-colon-str - (strip-spaces (substring string (match-end 0))))) - ))) - ) - ) - -(defun strip-spaces (string) - (if (= (string-to-char string) 32) - (if (= (length string) 1) "" - (strip-spaces (substring string 1))) - string) - ) - -(defun ct-load-project-emacs-files () - "Load project specific .emacs files" - (let ((ctprojs (getenv "CTPROJS"))) - (if ctprojs - (mapcar 'ct-load-project-emacs-file - (reverse (ct-break-space-colon-str ctprojs))) - )) - ) - -;; get all of the project specific .emacs files -(ct-load-project-emacs-files) diff --git a/dtool/src/attach/dtool.init b/dtool/src/attach/dtool.init deleted file mode 100644 index 89f62e7047..0000000000 --- a/dtool/src/attach/dtool.init +++ /dev/null @@ -1,10 +0,0 @@ -MODREL ETC_PATH built/etc -DOCSH source $DTOOL/built/etc/dtool.alias -DOCSH unsetenv LASTLOGIN -DOCSH setenv OS_VER `uname -r` -DOCSH ctshowprojs -DOSH source $DTOOL/built/etc/dtool.alias-sh -DOSH LASTLOGIN= -DOSH export LASTLOGIN -DOSH OS_VER=`uname -r` -DOSH export OS_VER diff --git a/dtool/src/attach/dtool.sh b/dtool/src/attach/dtool.sh deleted file mode 100755 index 598cd37cc0..0000000000 --- a/dtool/src/attach/dtool.sh +++ /dev/null @@ -1,81 +0,0 @@ -#! /bin/sh - -OS=`uname` -export OS - -# Setup the initial path -if [ $OS = "Linux" ]; then - PATH=/var/local/bin:~/bin:.:/usr/sbin:/sbin:/usr/bin:/bin:/usr/bin/X11:/usr/etc:/usr/local/bin -elif [ $OS = "IRIX64" ]; then - PATH=/var/local/bin:/usr/local/bin/ptools:~/bin:/usr/local/prman/bin:.:/usr/sbin:/usr/bsd:/sbin:/usr/bin:/bin:/usr/bin/X11:/usr/etc:/usr/demos/bin:/usr/local/bin -elif [ $OS = "CYGWIN_98-4.10" ]; then - PATH=/usr/local/bin:/bin:/CYGNUS/CYGWIN~1/H-I586~1/BIN:/WINDOWS:/WINDOWS:/WINDOWS/COMMAND:/DMI/BIN:/KATANA/UTL/DEV/MAKE:/KATANA/UTL/DEV/HITACHI -else - PATH=/var/local/bin:/usr/local/bin/ptools:~/bin:/usr/local/prman/bin:.:/usr/sbin:/usr/bsd:/sbin:/usr/bin:/bin:/usr/bin/X11:/usr/etc:/usr/demos/bin:/usr/local/bin -fi - -# Setup the initial manpath -#if [ $OS = "Linux" ]; then -# MANPATH=/usr/local/man:/usr/man/preformat:/usr/man:/usr/X11R6/man -#elif [ $OS = "IRIX64" ]; then -# MANPATH=/usr/share/catman:/usr/catman:/usr/local/share/catman:/usr/local/share/man:/usr/local/man -#elif [ $OS = "CYGWIN_98-4.10" ]; then -# MANPATH=/usr/local/man -#else -# MANPATH=/usr/share/catman:/usr/catman:/usr/local/share/catman:/usr/local/share/man:/usr/local/man -#fi -#export MANPATH - -LD_LIBRARY_PATH="." -export LD_LIBRARY_PATH -DYLD_LIBRARY_PATH="." -export DYLD_LIBRARY_PATH -CT_INCLUDE_PATH="." -export CT_INCLUDE_PATH -#cdpath=. -#CDPATH="." -#export CDPATH -DC_PATH="." -export DC_PATH -SSPATH="." -export SSPATH -STKPATH="." -export STKPATH -SHELL_TYPE="sh" -export SHELL_TYPE - -if [ -e /usr/atria ]; then - if /usr/atria/bin/cleartool mount -all > /dev/null 2>&1; then - HAVE_ATRIA=yes - export HAVE_ATRIA - fi -fi - -if [ -z "$CTDEFAULT_FLAV" ]; then - CTDEFAULT_FLAV="default" - export CTDEFAULT_FLAV -fi - -if [ -z "$DTOOL" ]; then - DTOOL=/beta/player/bootstrap/tool - export DTOOL -fi - -if [ -z "$PENV" ]; then - if [ $OS = "Linux" ]; then - PENV="Linux" - elif [ $OS = "IRIX64" ]; then - PENV="SGI" - elif [ $OS = "CYGWIN_98-4.10" ]; then - PENV="WIN32_DREAMCAST" - else - PENV="SGI" - fi -fi -export PENV - -if [ -z "$1" ]; then - source `$DTOOL/built/bin/ctattach.drv dtool default` -else - source `$DTOOL/built/bin/ctattach.drv dtool $1` -fi diff --git a/dtool/src/attach/get-cttree b/dtool/src/attach/get-cttree deleted file mode 100755 index d0e68e4f3f..0000000000 --- a/dtool/src/attach/get-cttree +++ /dev/null @@ -1,104 +0,0 @@ -#! /bin/sh -# -# get-cttree.sh -# -# Usage: -# -# get-cttree.sh [opts] output-file.tgz -# -# This script must be executed from within a project tree. -# -# Options: -# -# None at present. -# -#ENDCOMMENT - -while getopts "h" flag; do - case $flag in - h) sed '/#ENDCOMMENT/,$d' <$0 >&2 - exit 1;; - \?) exit 1; - esac -done - -shift `expr $OPTIND - 1` -output=$1 -projroot=`ctproj -r` - -if [ -z "$projroot" ]; then - echo "" - echo "You must execute this script in a project tree." - echo "" - exit 1 -fi - -if [ -z "$output" ]; then - sed '/#ENDCOMMENT/,$d' <$0 >&2 - exit 1 -fi - - -# Perform some sanity checks on input parameters. - -if [ ! -d "$projroot" ]; then - echo "" - echo "$projroot is not a directory!" - echo "" - exit 1 -fi - -if [ `basename $output .tgz` = `basename $output` ]; then - echo "" - echo "$output should end in .tgz" - echo "" - exit 1 -fi - -if [ ! -d /usr/atria ]; then - echo "" - echo "This script is intended to be run on an actual ClearCase vobs." - echo "" - exit 1 -fi - -projname=`basename $projroot` -projtop=`dirname $projroot` - -if [ "$projname" = "tool" ]; then - echo "" - echo "This script should not be used on the tool tree." - echo "" - exit 1 -fi - -if [ -f "$output" ]; then - if rm -i $output; then - echo "" - else - echo "Not overwriting $output" - exit 1 - fi -elif [ -r "$output" -o -w "$output" ]; then - echo "Cannot overwrite $output" - exit 1 -else - echo "" -fi - -# Check to make sure the local machine doesn't have anything checked out. -cd $projroot -outfile=/tmp/gc.$username.$projname.out -cleartool lsco -s -me -recurse >$outfile -if [ -s $outfile ]; then - echo "" - echo "Cannot build tarball; files still checked out in vobs:" - sed 's/^/ /;s/\.ct0\.//' $outfile - rm -f $outfile - echo "" - exit 1 -fi -rm -f $outfile - -(cd $projtop; cleartool find $projname -nxn -print | grep -v '/lost+found' | cpio -H tar -v -o | gzip) >$output - diff --git a/dtool/src/attach/get-delta b/dtool/src/attach/get-delta deleted file mode 100755 index 5ddb09a6a1..0000000000 --- a/dtool/src/attach/get-delta +++ /dev/null @@ -1,481 +0,0 @@ -#! /usr/local/bin/bash -# -# get-delta.sh -# -# Usage: -# -# get-delta.sh [opts] output-file.sh [file ...] -# -# This script must be executed from within a project tree. It -# examines the set of files that have been checked out (via neartool) -# and modified, and generates a script file that can be used to apply -# the changes made back to the main ClearCase vobs. -# -# By default, it generates a script for all checked-out files. You -# can restrict its operation to certain files and/or directories by -# listing them on the command line. -# -# Options: -# -# -c collapse versioning information on local copy after completion. -# Use this option with caution, as it is irreversible (and -# noninterruptible). Once the versioning information has been -# collapsed, it will be impossible to regenerate a script -# representing the changes that have been made locally; you -# should only do this when you are sure that your changes have -# been successfully applied to the other end. -# -# On the other hand, if you forget to run get-delta with -c after -# you have successfully applied your changes, you may -# inadvertently attempt to apply them again if you subsequently -# try to apply more changes. -# -#ENDCOMMENT - -function usage { - sed '/#ENDCOMMENT/,$d' <$0 >&2 - exit 1 -} - -# -# list_comments ( dirname basename ) -# -# Writes to stdout any comments associated with checked-out versions of -# the indicated file, in order. -# -function list_comments { - local dirname=$1 - local basename=$2 - local filename=$dirname/$basename - local file comment version - - if [ -f $dirname/.ct0.$basename ]; then - # Now look for comments, in version-number order. - - # We use a series of ls commands so we don't try to sort the - # filenames between the one-, two-, and three-digit version - # numbers. - - for file in `(cd $dirname; ls .ct[0-9].$basename; ls .ct[0-9][0-9].$basename; ls .ct[0-9][0-9][0-9].$basename) 2>/dev/null`; do - version=`echo $file | sed "s/^\.ct\([0-9]*\).*$/\1/"` - comment=$dirname/.ct${version}comment.$basename - if [ -f $comment ]; then - cat $comment - fi - done - fi -} - -# -# get_fullpath ( local_dir ) -# -# Sets $fullpath to the fully-qualified pathname associated with $local_dir. -# -function get_fullpath { - local local_dir=$1 - - if [ -z "$local_dir" ]; then - fullpath=`pwd` - else - if [ ! -d "$local_dir" ]; then - echo "Invalid directory: $local_dir" 1>&2 - exit 1 - fi - # If we use pwd instead of /bin/pwd, $PWD will be used, which will give - # the wrong answer - fullpath=`(cd $local_dir; /bin/pwd)` - fi -} - - -# -# get_rel_dir ( root_dir local_dir ) -# -# Sets $rel_dir to the string which represents $local_dir relative to -# $root_dir. This is a simple string-prefix operation, and could fail -# in some obscure cases. -# -function get_rel_dir { - get_fullpath $1 - local root_dir=$fullpath - - get_fullpath $2 - local local_dir=$fullpath - - # Now remove the initial prefix. - if [ "$root_dir" = "$local_dir" ]; then - rel_dir="." - else - rel_dir=`echo $local_dir | sed 's:^'$root_dir/'::'` - - if [ "$rel_dir" = "$local_dir" ]; then - echo "$local_dir is not a directory within $root_dir." 1>&2 - exit 1 - fi - fi -} - - -collapse= -while getopts "ch" flag; do - case $flag in - c) collapse=y;; - h) usage;; - \?) exit 1; - esac -done - -shift `expr $OPTIND - 1` -output=$1 -shift -projroot=`ctproj -r` - -if [ -z "$projroot" ]; then - echo "You must execute this script in a project tree." - exit 1 -fi - -if [ -z "$output" ]; then - usage -fi - - -# Perform some sanity checks on input parameters. - -if [ ! -d "$projroot" ]; then - echo "$projroot is not a directory!" - exit 1 -fi - -if [ `basename $output .sh` = `basename $output` ]; then - echo "$output should end in .sh" - exit 1 -fi - -if [ -f "$output" ]; then - rm -i $output - if [ -f "$output" ]; then - echo "Not overwriting $output" - exit 1 - fi -elif [ -e "$output" ]; then - echo "Cannot overwrite $output" - exit 1 -fi - -echo "" - -# Temporary files we'll build up as we process the files. - -base=`basename $output` -temp_ct0=/tmp/gd.ct0.$base -temp_checkout=/tmp/gd.checkout.$base -temp_dirs=/tmp/gd.dirs.$base -temp_files=/tmp/gd.files.$base -temp_diffs=/tmp/gd.diffs.$base - -rm -f $temp_ct0 $temp_checkout $temp_dirs $temp_files $temp_diffs -touch $temp_ct0 $temp_dirs $temp_files - - -# Get the list of files we'll want to delta in. - -if [ $# -eq 0 ]; then - # No explicit files, get all of them. - (cd $projroot; find . -name .ct0.\* -print) >>$temp_ct0 -else - # An explicit list of files. - for filename in $*; do - if [ -f $filename ]; then - dirname=`dirname $filename` - basename=`basename $filename` - - if [ -f $dirname/.ct0.$basename ]; then - get_rel_dir $projroot $dirname - echo ./$rel_dir/.ct0.$basename >>$temp_ct0 - else - echo $filename has no versions. - fi - - elif [ -d $filename ]; then - get_rel_dir $projroot $filename - - (cd $projroot; find ./$rel_dir -name .ct0.\* -print) >>$temp_ct0 - - else - echo $filename not found. - fi - done -fi - - -# Now start to build up the script. - -echo "#! /bin/sh" >$output -chmod 755 $output - -if [ ! -w $output ]; then - echo "Cannot write to $output!" - exit 1 -fi - -projname=`basename $projroot` - -# This part we cat in quoted, verbatim. -cat << 'EOF' >>$output - -any_opts= -list= -checkout= -patch= -cleanup= -checkin= -delta= -help= -while getopts "lopcidfh" flag; do - any_opts=y - only_list=y - case $flag in - l) list=y;; - o) checkout=y; only_list=;; - p) patch=y; only_list=;; - c) cleanup=y; only_list=;; - i) checkin=y; only_list=;; - d) delta=y; only_list=;; - f) checkout=y - patch=y - cleanup=y - checkin=y - delta=y - only_list=;; - h) help=y;; - \?) exit 1; - esac -done - -EOF - -# This part we cat in unquoted, so we can substitute the projname -# variable. - -cat << EOFOUTER >>$output -if [ \$help ]; then -cat << 'EOF' - -This patch file was generated using get-delta on a remote $projname tree. -It's designed to be run one time to apply the changes made remotely -back to the main branch of the tree. - -It should be run from within your own view, somewhere within the -$projname hierarchy on the ClearCase system. - -Options: - - -l List information about the patch file, including the creation - date and the list of modified files. - - -o Checkout all the relevant files and perform other ClearCase - operations (like renaming, creating, and removing files). - - -p Apply the relevant patches to all files after they have been - checked out. If this operation fails, the rest of the script - will not continue. - - -c Cleanup after successfully patching by removing .orig and .rej - files. - - -i Checkin modified files after successfully patching. - - -d Perform final merge by executing ctdelta on modified files. - - -f Perform full checkout/patch/merge cycle. This is equivalent to - specifying -opcid. - - -h This help page. - -If no options are specified, the default is -opc. - -EOF -exit 0 -fi - -if [ -z "\$any_opts" ]; then - checkout=y - patch=y - cleanup=y -fi -EOFOUTER - -any_merged= - -# We start with the commands given in the project's .ctcmds file. -ctcmds=$projroot/.ctcmds -if [ -f $ctcmds ]; then - any_merged=y - cat $ctcmds >>$temp_checkout - if [ $collapse ]; then - rm $ctcmds - fi -fi - - -for ct0 in `cat $temp_ct0`; do - dir=`dirname $ct0` - base=`basename $ct0 | sed 's/^\.ct0\.//'` - file=$dir/$base - ctnew=$projroot/$dir/.ctnew.$base - - if (cd $projroot; diff -u $ct0 $file >>$temp_diffs); then - # If diff returned success, it means the files were identical. In - # this case, don't bother checking it out. - echo "$file is unchanged." - - else - # Otherwise, the files were genuinely different. Check it out on - # the remote end. - echo $file - - if [ -f $ctnew ]; then - # This file is newly created. We need to create an element for - # it on the remote end. - eltype=`cat $ctnew` - if grep -x $dir $temp_dirs >/dev/null; then - echo directory already checked out >/dev/null - else - echo "ctco -nc $dir" >>$temp_checkout - echo $dir >> $temp_dirs - fi - echo "touch $file" >>$temp_checkout - echo "ctmkelem -eltype $eltype $file << 'EOF' 2>/dev/null" >>$temp_checkout - list_comments $projroot/$dir $base >>$temp_checkout - echo EOF >>$temp_checkout - - echo $file >> $temp_files - else - echo "ctco $file << 'EOF' 2>/dev/null" >>$temp_checkout - list_comments $projroot/$dir $base >>$temp_checkout - echo EOF >>$temp_checkout - - echo $file >> $temp_files - fi - fi - - if [ $collapse ]; then - echo Collapsing $file - neartool collapse $projroot/$file - fi -done -echo "" - -# Handle -l, list files modified -echo "" >>$output -echo 'if [ $list ]; then' >>$output -echo ' echo ""' >>$output -echo ' echo Patch file for '$projname' tree, built on '`date` >> $output -echo ' echo File was built by '`whoami` on `hostname` >>$output -echo ' echo ""' >>$output -echo ' echo Affected files are:' >>$output -echo ' cat <>$output -sed "s/^\./ $projname/" $temp_files >>$output -echo 'EOF' >>$output -echo ' echo ""' >>$output -echo 'fi' >>$output - -# Everything else depends on being in the proper tree. - -cat <>$output -if [ \$only_list ]; then - exit 0 -fi - -projroot=\`ctproj -r\` -if [ -z "\$projroot" ]; then - echo "" - echo "You must execute this script within the $projname tree." - echo "" - exit 1 -fi -if [ \`basename \$projroot\` != "$projname" ]; then - echo "" - echo "This script is intended for the $projname tree." - echo "" - exit 1 -fi -if [ ! -d /usr/atria ]; then - echo "" - echo "This script is intended to be run on an actual ClearCase vobs." - echo "" - exit 1 -fi -tmpfile=\`whoami\`-merge-$projname.tmp -cd \$projroot -touch \$tmpfile -EOF - - -# Handle -o, checkout stuff (and perform general ClearCase changes) -echo "" >>$output -echo 'if [ $checkout ]; then' >>$output -if [ -f $temp_checkout ]; then - cat $temp_checkout >> $output -else - echo 'echo Nothing to checkout.' >>$output -fi -echo 'fi' >>$output - -# Handle -p, apply patch -echo "" >>$output -echo 'if [ $patch ]; then' >>$output -if [ -f $temp_diffs ]; then - any_merged=y; - - echo " echo ''" >> $output - echo " echo Applying patches." >> $output - echo " if sed 's/^X//' << 'EOF' | patch -fsu; then" >>$output - - sed 's/^/X/' < $temp_diffs >>$output - echo EOF >>$output - - echo " echo All patches applied successfully." >>$output - echo " echo ''" >>$output - echo " else" >>$output - echo " echo Some conflicts detected:" >>$output - echo " find . -name '*.rej' -newer \$tmpfile -print" >>$output - echo " rm -f \$tmpfile" >>$output - echo " exit 1" >>$output - echo " fi" >>$output -else - echo ' echo No patches to apply.' >>$output -fi -echo 'fi' >>$output -echo "rm -f \$tmpfile" >>$output - -# Handle -c, cleanup -echo "" >>$output -echo 'if [ $cleanup ]; then' >>$output -sed 's/^\(.*\)$/ rm -f \1.orig \1.rej/' <$temp_files >>$output -echo 'fi' >>$output - -# Handle -i, checkin -echo "" >>$output -echo 'if [ $checkin ]; then' >>$output -sed 's/^\(.*\)$/ ctci -nc \1/' <$temp_files >>$output -sed 's/^\(.*\)$/ ctci -nc \1/' <$temp_dirs >>$output -echo 'fi' >>$output - -# Handle -d, delta -echo "" >>$output -echo 'if [ $delta ]; then' >>$output -sed 's/^\(.*\)$/ ctdelta \1/' <$temp_files >>$output -sed 's/^\(.*\)$/ ctdelta \1/' <$temp_dirs >>$output -echo 'fi' >>$output - -rm -f $temp_ct0 $temp_checkout $temp_dirs $temp_files $temp_diffs - -if [ -z "$any_merged" ]; then - echo "Nothing to do!" - echo "" - rm -f $output - exit 1 -fi - diff --git a/dtool/src/attach/neartool b/dtool/src/attach/neartool deleted file mode 100755 index a737597238..0000000000 --- a/dtool/src/attach/neartool +++ /dev/null @@ -1,1056 +0,0 @@ -#! /usr/local/bin/bash -# -# neartool - a Clearcase emulator for working off-line. -# -# Usage: -# -# neartool mkelem [-c "comment"] [-nc] [-eltype type] filename -# Marks a new file as a versioned element and checks it out. -# -# neartool mkdir [-c "comment"] [-nc] new_dir -# Creates a new Clearcase directory. -# -# neartool mv old-file new-file -# Renames or moves a versioned element. -# -# neartool mv file1 file2 file3 ... to-dir -# Moves a number of versioned elements into a new directory. -# -# neartool (co|checkout) [-c "comment"] [-nc] filename -# "checks out" a file by saving its current version and making a new -# version writable. The file is also marked as "checked-out". -# -# neartool (unco|uncheckout) filename -# Reverses the effect of the previous checkout. -# -# neartool (ci|checkin) [-c "comment"] [-nc] filename -# Marks the file as "checked in" and finalizes its most recent -# version, making it read-only. -# -# neartool revert filename -# Backs up a version on a checked-in filename. -# -# neartool revertall filename -# Reverts a filename all the way to its original state as extracted -# from the Clearcase vobs. -# -# neartool collapse filename -# Collapse all versioning information and mark the file as -# "checked-in" and merged as is. Presumably this should only be -# done after merging the changes back into the actual clearcase -# vobs. -# -# neartool rmname filename -# Removes a filename from a Clearcase directory. -# -# neartool find root [opts] -# Finds all elements with versions at root or below. The options -# are ignored. This is just a hack to support ctihave. -# -# neartool (lsco|lscheckout) [-l] file1 file2 file3... -# Lists which of the files are currently checked out, if any. If no -# filenames are given, lists all of the checked out files in the -# current directory. The -l option is ignored. -# -# neartool diff [-pre] filename -# Performs a diff between the indicated filename and its previous -# version. The option -pre is ignored. -# -# neartool xdiff [-pre] filename -# Performs an xdiff between the indicated filename and its previous -# version. The option -pre is ignored. -# -#ENDCOMMENT - - -# -# get_nth_param ( param-no param1 param2 param3 ... ) -# -# Sets the variable nth_param to the value of the parameter -# corresponding to param-no. -# -function get_nth_param { - local param_no=$1 - shift $param_no - nth_param=$1 -} - -# -# get_last_param ( param1 param2 param3 ... ) -# -# Sets the variable last_param to the value of the last non-blank -# parameter. -# -function get_last_param { - last_param="$1" - shift - while [ ! -z "$*" ]; do - last_param="$1" - shift - done -} - -# -# format_comment ( comment_str ) -# -# Writes the parameter -c '$comment_str', or -nc if the comment is -# empty, to the standard output. Handles nested quotes appropriately. -# -function format_comment { - local comment="$*" - - if [ -z "$comment" ]; then - echo "-nc" - else - # Escaping single quotes inside a single-quoted string doesn't - # seem to work too reliably. Instead, we'll just remove single - # quotes. So there. - - # This absurd number of backslashes is needed to output a single - # backslash through all this nesting. - # echo "-c '"`echo $comment | sed "s/'/\\\\\\\\'/g"`"'" - echo "-c '"`echo $comment | sed "s/'//g"`"'" - fi -} - -# -# list_comments ( dirname basename ) -# -# Writes to stdout any comments associated with checked-out versions of -# the indicated file, in order. -# -function list_comments { - local dirname=$1 - local basename=$2 - local filename=$dirname/$basename - local file comment version - - if [ -f $dirname/.ct0.$basename ]; then - # Now look for comments, in version-number order. - - # We use a series of ls commands so we don't try to sort the - # filenames between the one-, two-, and three-digit version - # numbers. - - for file in `(cd $dirname; ls .ct[0-9].$basename; ls .ct[0-9][0-9].$basename; ls .ct[0-9][0-9][0-9].$basename) 2>/dev/null`; do - version=`echo $file | sed "s/^\.ct\([0-9]*\).*$/\1/"` - comment=$dirname/.ct${version}comment.$basename - if [ -f $comment ]; then - sed s'/^/ /' <$comment - fi - done - fi -} - -# -# get_highest_version ( dirname basename ) -# -# Sets the variable $version to the numeric value that is the highest -# existing version number defined for the given filename. If the -# filename has no previous version numbers, sets $version to -1. -# -function get_highest_version { - local dirname=$1 - local basename=$2 - local filename=$dirname/$basename - local last - - if [ -f $dirname/.ct0.$basename ]; then - # If there are any versions at all, get the highest-numbered one - # and return it. - - # We use a series of ls commands so we don't try to sort the - # filenames between the one-, two-, and three-digit version - # numbers. - - last=`(cd $dirname; ls .ct[0-9].$basename; ls .ct[0-9][0-9].$basename; ls .ct[0-9][0-9][0-9].$basename) 2>/dev/null | tail -1` - version=`echo $last | sed "s/^\.ct\([0-9]*\).*$/\1/"` - else - # If there aren't any versions yet, the highest-numbered existing - # version number is -1. - version=-1 - fi -} - -# -# is_ctinternal ( basename ) -# -# Returns success if the filename matches one of the internal filenames -# generated by this script, failure if it is a perfectly ordinary -# non-ct file. -# -function is_ctinternal { - local basename=$1 - - case $basename in - .ct[0-9].*|ct[0-9][0-9].*|ct[0-9][0-9][0-9].*|\ - .ct[0-9]comment.*|.ct[0-9][0-9]comment.*|.ct[0-9][0-9][0-9]comment*|\ - .ctco.*|.ctnew.*) - return 0;; - esac - return 1 -} - -# -# is_install_dir ( dirname ) -# -# Returns success if the directory is any of the directories known to -# be install directories, such as inc, lib, lib/ss, etc. -# -function is_install_dir { - local dirname=$1 - - get_rel_dir $projroot $dirname - - case $rel_dir in - inc) return 0;; - inc/*) return 0;; - include/*) return 0;; - lib) return 0;; - lib/*) return 0;; - esac - return 1 -} - -# -# rm_all_ctinternals ( dirname basename ) -# -# Removes all the ctinternal files associated with the indicated file. -# -function rm_all_ctinternals { - local dirname=$1 - local basename=$2 - - rm -f $dirname/.ct[0-9].$basename - rm -f $dirname/.ct[0-9][0-9].$basename - rm -f $dirname/.ct[0-9][0-9][0-9].$basename - rm -f $dirname/.ct[0-9]comment.$basename - rm -f $dirname/.ct[0-9][0-9]comment.$basename - rm -f $dirname/.ct[0-9][0-9][0-9]comment.$basename - rm -f $dirname/.ctnew.$basename - rm -f $dirname/.ctco.$basename -} - -# -# sane_filename ( dirname basename ) -# -# Performs some sanity checks on the filename. If the file is -# unusable, exits the script with a status of 1. -# -function sane_filename { - local dirname=$1 - local basename=$2 - local filename=$dirname/$basename - - if [ -z "$basename" ]; then - echo "Filename unspecified." 1>&2 - exit 1 - fi - - if is_ctinternal $basename; then - echo "$filename is an internal filename and should not be checked out." 1>&2 - exit 1 - fi - - # It's easy to accidentally attempt to check something out from the - # inc directory. Let's detect this. - - if is_install_dir $dirname; then - echo "$basename is in an install directory." 1>&2 - echo "It should probably not be checked out." 1>&2 - exit 1 - fi - - if [ -d $filename ]; then - # Let's just quietly ignore directories. If we issue the warning - # statement, it makes ctaddtgt and ctaddpkg seem like they're - # failing. - # echo "Cannot operate on directories." 1>&2 - exit 0 - fi -} - -# -# get_fullpath ( local_dir ) -# -# Sets $fullpath to the fully-qualified pathname associated with $local_dir. -# -function get_fullpath { - local local_dir=$1 - - if [ -z "$local_dir" ]; then - fullpath=`pwd` - else - if [ ! -d "$local_dir" ]; then - echo "Invalid directory: $local_dir" 1>&2 - exit 1 - fi - # If we use pwd instead of /bin/pwd, $PWD will be used, which will give - # the wrong answer - fullpath=`(cd $local_dir; /bin/pwd)` - fi -} - - -# -# get_rel_dir ( root_dir local_dir ) -# -# Sets $rel_dir to the string which represents $local_dir relative to -# $root_dir. This is a simple string-prefix operation, and could fail -# in some obscure cases. -# -function get_rel_dir { - get_fullpath $1 - local root_dir=$fullpath - - get_fullpath $2 - local local_dir=$fullpath - - # Now remove the initial prefix. - if [ "$root_dir" = "$local_dir" ]; then - rel_dir="." - else - rel_dir=`echo $local_dir | sed 's:^'$root_dir/'::'` - - if [ "$rel_dir" = "$local_dir" ]; then - echo "$local_dir is not a directory within $root_dir." 1>&2 - exit 1 - fi - fi -} - -function ctmkelem { - local filename dirname basename - local comment eltype - - eltype=text_file - while getopts c:n:e: flag; do - case $flag in - c) comment="$OPTARG";; - n) comment="";; - e) case $OPTARG in - ltype) get_nth_param $OPTIND "$@" - eltype=$nth_param - OPTIND=`expr $OPTIND + 1`;; - *) echo Invalid switch -e$OPTARG - exit 1; - esac;; - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - - ctco=$dirname/.ctco.$basename - if [ -f $ctco ]; then - echo "$filename is already checked out." 1>&2 - exit 1 - fi - - if [ ! -f $filename ]; then - # If the file doesn't exist, create it. - echo -n "" >$filename - fi - - get_highest_version $dirname $basename - - if [ $version -ne -1 ]; then - echo "$filename already has versions." 1>&2 - exit 1 - fi - - if [ ! -w $filename ]; then - echo "$filename has no write permission, it's probably already a versioned element." 1>&2 - exit 1 - fi - - # Create a ctnew file to indicate the file is newly created. - ctnew=$dirname/.ctnew.$basename - echo $eltype >$ctnew - chmod uga-w $ctnew - - # Now "check out" the new filename by creating an empty first version. - - next=$dirname/.ct0.$basename - nextcomment=$dirname/.ct0comment.$basename - - if [ ! -z "$comment" ]; then - echo $comment >$nextcomment - chmod uga-w $nextcomment - fi - echo -n "" > $next - - date >$ctco - done -} - -function ctmkdir { - local filename dirname basename - local comment - - while getopts c:n: flag; do - case $flag in - c) comment="$OPTARG";; - n) comment="";; - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - if [ -z "$basename" ]; then - echo "Filename unspecified." 1>&2 - exit 1 - fi - - if is_ctinternal $basename; then - echo "$filename is an internal filename and should not be checked out." 1>&2 - exit 1 - fi - - if mkdir $filename; then - # The directory is successfully made; record this in the - # Clearcase instructions. - get_rel_dir $projroot $dirname - echo "ctco -nc $rel_dir" >>$projroot/.ctcmds - echo "ctmkdir `format_comment $comment` $rel_dir/$basename" >>$projroot/.ctcmds - else - echo "Unable to create directory $filename." 1>&2 - fi - done -} - -# -# do_ctmv ( oldname newname ) -# -# The implementation of ctmv. This renames a single file, possibly -# placing it in a new directory. -# -function do_ctmv { - local oldfilename olddirname oldbasename - local newfilename newdirname newbasename - - oldfilename=$1 - olddirname=`dirname $oldfilename` - oldbasename=`basename $oldfilename` - - newfilename=$2 - newdirname=`dirname $newfilename` - newbasename=`basename $newfilename` - - sane_filename $olddirname $oldbasename - sane_filename $newdirname $newbasename - - if [ ! -f $oldfilename ]; then - echo "$oldfilename does not exist." 1>&2 - exit 1 - fi - - if [ -f $newfilename ]; then - echo "$newfilename already exists--remove it first." 1>&2 - exit 1 - fi - - oldctnew=$olddirname/.ctnew.$oldfilename - - if [ ! -f $oldctnew ]; then - # The filename exists on the actual Clearcase vobs. We need to - # record the renaming. - - get_rel_dir $projroot $olddirname - local oldroot=$rel_dir - get_rel_dir $projroot $newdirname - local newroot=$rel_dir - - echo "ctco -nc $oldroot" >> $projroot/.ctcmds - if [ "$oldroot" != "$newroot" ]; then - echo "ctco -nc $newroot" >> $projroot/.ctcmds - fi - echo "ctmv $oldroot/$oldbasename $newroot/$newbasename" >> $projroot/.ctcmds - fi - - # Now rename our local copy, and all of its version tracking stuff. - get_fullpath $newdirname - local newroot=$fullpath - - (cd $olddirname; - for file in \ - `ls .ct[0-9].$oldbasename \ - .ct[0-9][0-9].$oldbasename \ - .ct[0-9][0-9][0-9].$oldbasename \ - .ct[0-9]comment.$oldbasename \ - .ct[0-9][0-9]comment.$oldbasename \ - .ct[0-9][0-9][0-9]comment.$oldbasename \ - .ctnew.$oldbasename \ - .ctco.$oldbasename \ - $oldbasename 2>/dev/null`; do - ctprefix=`echo $file | sed 's:'$oldbasename'$::'` - mv -i $file $newroot/$ctprefix$newbasename - done) -} - -function ctmv { - local source destination - - while getopts "" flag; do - case $flag in - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - get_last_param "$@" - destination=$last_param; - - if [ ! -d "$destination" ]; then - # If we're not moving to a directory, we are renaming a file--and - # thus we can only allow two parameters. - source=$1 - shift 2 - if [ ! -z "$*" ]; then - echo Last filename must be a directory. - exit 1 - fi - - do_ctmv $source $destination - else - - # Otherwise, we're moving a slew of files to a directory. Get - # each filename and move it. - for source in "$@"; do - if [ "$source" != "$destination" ]; then - do_ctmv $source $destination/$source - fi - done - fi -} - -function ctco { - local filename dirname basename - - while getopts c:n: flag; do - case $flag in - c) comment="$OPTARG";; - n) comment="";; - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - okflag=y - - if [ ! -f $filename ]; then - echo "$filename does not exist." 1>&2 - okflag= - else - ctco=$dirname/.ctco.$basename - if [ -f $ctco ]; then - echo "$filename is already checked out." 1>&2 - okflag= - else - if [ -w $filename ]; then - echo "$filename has write permission, it's probably not a versioned element." 1>&2 - echo "Try neartool mkelem $filename." 1>&2 - okflag= - fi - fi - fi - - if [ ! -z "$okflag" ]; then - get_highest_version $dirname $basename - next=$dirname/.ct`expr $version + 1`.$basename - nextcomment=$dirname/.ct`expr $version + 1`comment.$basename - - if [ ! -z "$comment" ]; then - echo $comment >$nextcomment - chmod uga-w $nextcomment - fi - - mv $filename $next - cp $next $filename - chmod ug+w $filename - - date >$ctco - fi - done -} - - -function ctunco { - local ignore filename dirname basename - - while getopts "r:" flag; do - case $flag in - r) ignore=;; - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - - if [ ! -f $filename ]; then - echo "$filename does not exist." 1>&2 - exit 1 - fi - - ctco=$dirname/.ctco.$basename - if [ ! -f $ctco ]; then - echo "$filename is not checked out." 1>&2 - exit 1 - fi - - get_highest_version $dirname $basename - last=$dirname/.ct$version.$basename - lastcomment=$dirname/.ct${version}comment.$basename - - mv -f $last $filename - rm -f $lastcomment - rm $ctco - touch $filename # For emacs, and correct make behavior. - done -} - - -function ctci { - local filename dirname basename - local comment - - while getopts c:n: flag; do - case $flag in - c) comment="$OPTARG";; - n) comment="";; - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - - if [ ! -f $filename ]; then - echo "$filename does not exist." 1>&2 - exit 1 - fi - - - ctco=$dirname/.ctco.$basename - if [ ! -f $ctco ]; then - echo "$filename is not checked out." 1>&2 - exit 1 - fi - - # We touch the file, so emacs will note that it has changed. - touch $filename - chmod uga-w $filename - - rm $ctco - done -} - - -function ctrevert { - local filename dirname basename - - while getopts "" flag; do - case $flag in - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - - if [ ! -f $filename ]; then - echo "$filename does not exist." 1>&2 - exit 1 - fi - - ctco=$dirname/.ctco.$basename - if [ -f $ctco ]; then - echo "$filename is currently checked out. Use neartool unco $filename." 1>&2 - exit 1 - fi - - get_highest_version $dirname $basename - - if [ $version -eq -1 ]; then - echo "$filename has no versions." 1>&2 - exit 1 - fi - - last=$dirname/.ct$version.$basename - lastcomment=$dirname/.ct${version}comment.$basename - - mv -f $last $filename - rm -f $lastcomment - - if [ $version -eq 0 ]; then - ctnew=$dirname/.ctnew.$basename - if [ -f $ctnew ]; then - echo "Removing newly created element $filename." 1>&2 - rm -f $ctnew $filename - fi - fi - done -} - -function ctrevertall { - local filename dirname basename - - while getopts "" flag; do - case $flag in - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - - if [ ! -f $filename ]; then - echo "$filename does not exist." 1>&2 - exit 1 - fi - - ct0=$dirname/.ct0.$basename - if [ ! -f $ct0 ]; then - echo "$filename has no versions." 1>&2 - else - ctnew=$dirname/.ctnew.$basename - if [ ! -f $ctnew ]; then - # If we didn't newly create this file, preserve its original version. - mv -f $ct0 $filename - fi - rm_all_ctinternals $dirname $basename - fi - done -} - -function ctcollapse { - local filename dirname basename - - while getopts "" flag; do - case $flag in - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - - if [ ! -f $filename ]; then - echo "$filename does not exist." 1>&2 - exit 1 - fi - - rm_all_ctinternals $dirname $basename - - chmod uga-w $filename - touch $filename # For Emacs' benefit. - done -} - -function ctrmname { - local filename dirname basename - while getopts "" flag; do - case $flag in - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - - if [ ! -f $filename ]; then - echo "$filename does not exist." 1>&2 - exit 1 - fi - - ctnew=$dirname/.ctnew.$basename - - if [ ! -f $ctnew ]; then - # The filename exists on the actual Clearcase vobs. We need to - # record the removing. - - get_rel_dir $projroot $dirname - local root=$rel_dir - - echo "ctco -nc $root" >> $projroot/.ctcmds - echo "ctrm $root/$basename" >> $projroot/.ctcmds - fi - - # Now remove all of the versioned stuff. - ctcollapse $filename - - # And remove the file itself. - rm -f $filename - done -} - - -function ctfind { - local root=$1 - - if [ -z "$root" ]; then - echo "Specify a starting point of the find." 1>&2 - exit 1 - fi - - find $root -name .ct0.\* -print | sed 's/\.ct0\.//' -} - -function ctdescribe { - local filename dirname basename - - while getopts "" flag; do - case $flag in - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in `ls "$@"`; do - dirname=`dirname $filename` - basename=`basename $filename` - - ctco=$dirname/.ctco.$basename - get_highest_version $dirname $basename - version=`expr $version + 1` - - if [ -f $ctco ]; then - if [ $version -eq 0 ]; then - echo "$filename is checked out with no versions." - else - echo "$filename is checked out as version $version." - list_comments $dirname $basename - fi - else - if [ $version -eq 0 ]; then - echo "$filename has not been checked out and has no versions." - else - echo "$filename is checked in as version $version." - list_comments $dirname $basename - fi - fi - done -} - -function ctlsco { - local filename dirname basename - local ignore - while getopts "l" flag; do - case $flag in - l) ignore=;; - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - for filename in `ls "$@"`; do - dirname=`dirname $filename` - basename=`basename $filename` - - ctco=$dirname/.ctco.$basename - if [ -f $ctco ]; then - ctdescribe $filename - fi - done -} - -function ctdiff { - local filename dirname basename - local graphical - local ignore - local diff - - while getopts "gp:" flag; do - case $flag in - g) graphical=y;; - p) ignore=;; - \?) exit 1; - esac - done - shift `expr $OPTIND - 1` - - diff=diff - if [ $graphical ]; then - # The user requested a graphical difference; use gdiff if we can - # find it. Otherwise, fall back to diff. - if which gdiff 2>/dev/null >/dev/null; then - diff=gdiff - fi - fi - - if [ -z "$1" ]; then - echo "No filenames given." - exit 1 - fi - - for filename in "$@"; do - dirname=`dirname $filename` - basename=`basename $filename` - - sane_filename $dirname $basename - - if [ ! -f $filename ]; then - echo "$filename does not exist." 1>&2 - exit 1 - fi - - get_highest_version $dirname $basename - - if [ $version -eq -1 ]; then - echo "$filename has no versions." 1>&2 - exit 1 - fi - - last=$dirname/.ct$version.$basename - - # First, call diff (the real diff) just to see if the files are - # different at all. - - if diff $last $filename >/dev/null; then - echo Files are identical. - - else - # If the files ARE different, then invoke diff again (which - # might be gdiff, this time) to actually report the differences. - # We must do it twice like this because gdiff might not return - # non-zero if the files are different. - - $diff $last $filename - fi - done -} - - -function usage { - sed '/#ENDCOMMENT/,$d' <$0 >&2 - exit 1 -} - - -# -# Main entry point -# - -command=$1 -if [ -z "$command" ]; then - usage -fi - -shift - -projroot=`ctproj -r` - -if [ -z "$projroot" ]; then - echo "Not currently in a project tree." 1>&2 - exit 1 -fi - -case $command in - mkelem) ctmkelem "$@";; - mkdir) ctmkdir "$@";; - mv) ctmv "$@";; - co|checkout) ctco "$@";; - unco|uncheckout) ctunco "$@";; - ci|checkin) ctci "$@";; - revert) ctrevert "$@";; - revertall) ctrevertall "$@";; - collapse) ctcollapse "$@";; - rmname) ctrmname "$@";; - find) ctfind "$@";; - lsco|lscheckout) ctlsco "$@";; - describe) ctdescribe "$@";; - diff) ctdiff "$@";; - xdiff) ctdiff -g "$@";; - h|help|-h) usage;; - *) echo "Invalid option: $command" 1>&2 - exit 1; -esac diff --git a/dtool/src/attach/unco.pl b/dtool/src/attach/unco.pl deleted file mode 100644 index dd383ab99f..0000000000 --- a/dtool/src/attach/unco.pl +++ /dev/null @@ -1,31 +0,0 @@ -require "$tool/built/include/ctutils.pl" ; -require "$tool/built/include/ctdelta.pl" ; - -# Remove a branch for an element if needed -# input is in: -# $_[0] = element -sub CTUncoDoIt { - local( $elem ) = $_[0] ; - local( $ver ) = &CTDeltaGetVersion( $elem ) ; - if ( $ctdebug ne "" ) { - print STDERR "Unco script: got version '" . $ver . "'\n" ; - } - local( @verlist ) ; - @verlist = split( /\//, $ver ) ; - local( $vlast ) = pop( @verlist ) ; - if ( $ctdebug ne "" ) { - print STDERR "Unco script: last part of version is '" . $vlast . "'\n" ; - } - if ( $#verlist > 1 ) { - local( $branch ) = join( "/", @verlist ) ; - if ( $vlast == 0 ) { - local( $cmd ) = "cleartool rmbranch -force -nc $elem" . "@@" . "$branch" ; - if ( $ctdebug ne "" ) { - print STDERR "Unco script: command is '" . $cmd . "'\n" ; - } - system $cmd ; - } - } -} - -1; diff --git a/dtool/src/attach/update-cttree b/dtool/src/attach/update-cttree deleted file mode 100755 index 2abf87ee15..0000000000 --- a/dtool/src/attach/update-cttree +++ /dev/null @@ -1,189 +0,0 @@ -#! /bin/sh -# -# update-cttree.sh -# -# Usage: -# -# update-cttree.sh [opts] hostname -# -# Uses rsh and rdist to update the indicated host with a fresh copy of the -# current project tree. -# -# This script must be executed from within a project tree. -# -# Options: -# -# -u username Specify the login name on the remote host. -# -# -d dir Specify the player install dir on the remote host. This -# the directory above the project-tree-specific directory -# like 'panda' or 'tool'. The default is 'player'. -# -# -t Touch the build-request timestamp file after updating. -# This assumes there's a cron job running on the remote -# machine checking the date on this file from time to time. -# -# -f Assume the user knows what he/she is doing, and don't bother -# to check that there are no files checked out in the vobs -# before releasing. This can save considerable time when the -# system is extremely slow; however, it can be dangerous -# to accidentally release a checked-out file (because the -# file will then be write-access on the remote host, and -# neartool will not be able to track local changes made to it.) -# -#ENDCOMMENT - -username=`whoami` -dirname=player -touch_request= -cocky_user= - -while getopts "u:d:tfh" flag; do - case $flag in - u) username=$OPTARG;; - d) dirname=$OPTARG;; - t) touch_request=y;; - f) cocky_user=y;; - h) sed '/#ENDCOMMENT/,$d' <$0 >&2 - exit 1;; - \?) exit 1; - esac -done - -shift `expr $OPTIND - 1` -remote_host=$1 -projroot=`ctproj -r` - -if [ -z "$projroot" ]; then - echo "" - echo "You must execute this script in a project tree." - echo "" - exit 1 -fi - -if [ -z "$remote_host" ]; then - echo "" - echo "You must specify a remote hostname. -h for help." - echo "" - exit 1 -fi - -if [ ! -d /usr/atria ]; then - echo "" - echo "This script is intended to be run on an actual ClearCase vobs." - echo "" - exit 1 -fi - -projname=`basename $projroot` -projtop=`dirname $projroot` - -if [ "$projname" = "tool" ]; then - echo "" - echo "This script should not be used on the tool tree." - echo "" - exit 1 -fi - -outfile=/tmp/uc.$username.$projname.$remote_host.out -errfile=/tmp/uc.$username.$projname.$remote_host.err -rm -f $outfile $errfile - -# Check to make sure we can run rsh to the remote machine, and that -# the remote machine doesn't have anything checked out. - -if rsh $remote_host -l $username "cd $dirname; find $projname -name .ct0.\* -print" >$outfile 2>$errfile; then - if [ ! -f $outfile ]; then - echo "" - echo "Error in processing; unable to generate $outfile." - echo "" - rm -f $outfile $errfile - exit 1 - fi - if [ ! -f $errfile ]; then - echo "" - echo "Error in processing; unable to generate $errfile." - echo "" - rm -f $outfile $errfile - exit 1 - fi - if [ -s $errfile ]; then - echo "" - echo "Unable to scan project tree $dirname/$projname on $remote_host." - echo "" - rm -f $outfile $errfile - exit 1 - fi - if [ -s $outfile ]; then - echo "" - echo "Cannot update $remote_host; files still checked out on remote:" - sed 's/^/ /;s/\.ct0\.//' $outfile - rm -f $outfile $errfile - echo "" - exit 1 - fi -else - echo "" - echo "Cannot rsh to $remote_host as $username." - echo "" - rm -f $outfile $errfile - exit 1 -fi - -# Check to make sure the local machine doesn't have anything checked out. -if [ -z "$cocky_user" ]; then - cd $projroot - cleartool lsco -s -me -recurse >$outfile - if [ -s $outfile ]; then - echo "" - echo "Cannot update from "`hostname`"; files still checked out in vobs:" - sed 's/^/ /;s/\.ct0\.//' $outfile - rm -f $outfile $errfile - echo "" - exit 1 - fi -fi - -rm -f $outfile $errfile - - -# -# Get the complete list of files in the tree we need to update. -# -cd $projtop -filelist=${outfile}.files -rm -f $filelist -cleartool find $projname -nxn -print | grep -v '/lost+found' > $filelist - -# -# Now build up a number of rdist files, as needed, to update these files. -# We have to do this in stages because there seems to be a limit of about -# 2000 files in one rdist file. -# -numlines=`wc -l $filelist | awk '{ print $1 }'` -echo $projname contains $numlines files. - -startline=1 -while [ $startline -le $numlines ]; do - echo "FILES = (" >> $outfile - tail +$startline $filelist | head -2000 >> $outfile - echo ")" >> $outfile - - echo '${FILES} -> '$username@$remote_host >>$outfile - echo " install $dirname;" >> $outfile - - if [ $touch_request ]; then - echo " cmdspecial \"touch $dirname/$projname/build-request\" ;" >>$outfile - fi - - if rdist -onochkowner,nochkgroup,numchkgroup,whole,nodescend -f $outfile; then - rm -f $outfile - else - echo "Error in rdist." - rm -f $outfile $filelist $errfile - exit 1 - fi - startline=`expr $startline + 2000` -done - -rm -f $filelist $errfile From 705fd445f3079853dff487e5a206017a62da8ef4 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Sun, 25 Feb 2018 19:05:45 -0700 Subject: [PATCH 109/118] dtool: Fix an inconsistent EXPCL/EXPTP --- dtool/src/dtoolutil/vector_string.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dtool/src/dtoolutil/vector_string.cxx b/dtool/src/dtoolutil/vector_string.cxx index 2b7ba4202a..0e5df2ce1d 100644 --- a/dtool/src/dtoolutil/vector_string.cxx +++ b/dtool/src/dtoolutil/vector_string.cxx @@ -13,8 +13,8 @@ #include "vector_string.h" -#define EXPCL EXPCL_DTOOLCONFIG -#define EXPTP EXPTP_DTOOLCONFIG +#define EXPCL EXPCL_DTOOL +#define EXPTP EXPTP_DTOOL #define TYPE std::string #define NAME vector_string From 01b08357973d7b6211228bf020b70cfcccc593f8 Mon Sep 17 00:00:00 2001 From: Sam Edwards Date: Mon, 26 Feb 2018 00:15:38 -0700 Subject: [PATCH 110/118] pandabase: Remove reference to DIRECTORY_DLLS Nothing defines this under any circumstances. Also this moves the various component EXPCLs and EXPTPs within the include guard of the entire header. --- panda/src/pandabase/pandasymbols.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/panda/src/pandabase/pandasymbols.h b/panda/src/pandabase/pandasymbols.h index cc02ff0e12..6c817f56bd 100644 --- a/panda/src/pandabase/pandasymbols.h +++ b/panda/src/pandabase/pandasymbols.h @@ -326,12 +326,6 @@ #define INLINE_GUI INLINE #define INLINE_AUDIO INLINE -#endif - - -#if defined(DIRECTORY_DLLS) - -#else #define EXPCL_PANDA_PGRAPH EXPCL_PANDA #define EXPTP_PANDA_PGRAPH EXPTP_PANDA From 5523af09ce24ea61e4a89756acc9d7bc9c7d727f Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 27 Feb 2018 12:05:23 +0100 Subject: [PATCH 111/118] makepanda: rebuild modules when interrogate_module is modified --- makepanda/makepandacore.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 0bce3f4450..81163c8179 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -3419,5 +3419,9 @@ def TargetAdd(target, dummy=0, opts=[], input=[], dep=[], ipath=None, winrc=None t.deps[FindLocation("interrogate.exe", [])] = 1 t.deps[FindLocation("dtool_have_python.dat", [])] = 1 + if target.endswith(".obj") and any(x.endswith(".in") for x in input): + if not CrossCompiling(): + t.deps[FindLocation("interrogate_module.exe", [])] = 1 + if target.endswith(".pz") and not CrossCompiling(): t.deps[FindLocation("pzip.exe", [])] = 1 From b62435a37384a3be475bbb80c7dd00c59a9b8ae4 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 27 Feb 2018 15:02:49 +0100 Subject: [PATCH 112/118] general: remove all uses of deprecated upcastTo*() methods These methods have not been needed for a very long time, so we should not encourage their use. --- contrib/src/sceneeditor/seFileSaver.py | 8 ++++---- contrib/src/sceneeditor/seLights.py | 9 +-------- direct/src/showbase/ShadowDemo.py | 4 ++-- direct/src/showbase/ShowBase.py | 3 +-- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/contrib/src/sceneeditor/seFileSaver.py b/contrib/src/sceneeditor/seFileSaver.py index 73c71f5901..317c9427f2 100644 --- a/contrib/src/sceneeditor/seFileSaver.py +++ b/contrib/src/sceneeditor/seFileSaver.py @@ -488,7 +488,7 @@ class FileSaver: out_file.write (i2+ "alight = AmbientLight(\'"+ light.getName() +"\')\n") out_file.write (i2+ "alight.setColor(VBase4("+ str(light.getLightColor().getX())+ "," + str(light.getLightColor().getY())+ "," + str(light.getLightColor().getZ()) + "," + str(light.getLightColor().getW()) + "))\n") out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n") - out_file.write (i2+ "self."+light.getName()+"= render.attachNewNode(alight.upcastToPandaNode())\n") + out_file.write (i2+ "self."+light.getName()+"= render.attachNewNode(alight)\n") out_file.write (i2+ "self."+light.getName()+".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n") out_file.write (i2+ "self.LightDict[\'" + light.getName() + "\']=alight\n") out_file.write (i2+ "self.LightTypes[\'" + light.getName() + "\']=\'" + type + "\'\n") @@ -503,7 +503,7 @@ class FileSaver: #out_file.write (i2+ "alight.setPoint(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n") out_file.write (i2+ "alight.setSpecularColor(Vec4(" + str(light.getSpecColor().getX()) + "," + str(light.getSpecColor().getY()) + "," + str(light.getSpecColor().getZ()) + "," + str(light.getSpecColor().getW()) + "))\n") out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n") - out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight.upcastToPandaNode())\n") + out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight)\n") out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n") out_file.write (i2+ "self."+light.getName()+ ".setHpr(Vec3("+ str(light.getH())+ "," + str(light.getP())+ "," + str(light.getR()) + "))\n") out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n") @@ -521,7 +521,7 @@ class FileSaver: out_file.write (i2+ "alight.setSpecularColor(Vec4(" + str(light.getSpecColor().getX()) + "," + str(light.getSpecColor().getY()) + "," + str(light.getSpecColor().getZ()) + "," + str(light.getSpecColor().getW()) + "))\n") out_file.write (i2+ "alight.setAttenuation(Vec3("+ str(light.getAttenuation().getX()) + "," + str(light.getAttenuation().getY()) + "," + str(light.getAttenuation().getZ()) + "))\n") out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n") - out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight.upcastToPandaNode())\n") + out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight)\n") out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n") out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n") out_file.write (i2+ "self.LightDict[\'" + light.getName() + "\']=alight\n") @@ -539,7 +539,7 @@ class FileSaver: out_file.write (i2+ "alight.setAttenuation(Vec3("+ str(light.getAttenuation().getX()) + "," + str(light.getAttenuation().getY()) + "," + str(light.getAttenuation().getZ()) + "))\n") out_file.write (i2+ "alight.setExponent(" +str(light.getExponent()) +")\n") out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n") - out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight.upcastToLensNode())\n") + out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight)\n") out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n") out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n") out_file.write (i2+ "self."+light.getName()+ ".setHpr(Vec3("+ str(light.getH())+ "," + str(light.getP())+ "," + str(light.getR()) + "))\n") diff --git a/contrib/src/sceneeditor/seLights.py b/contrib/src/sceneeditor/seLights.py index 1a42e9045a..ccc58ce0cd 100644 --- a/contrib/src/sceneeditor/seLights.py +++ b/contrib/src/sceneeditor/seLights.py @@ -63,13 +63,8 @@ class seLight(NodePath): self.lence = lence self.active = True - if isinstance(light, Spotlight): - node = light.upcastToLensNode() - else: - node = light.upcastToPandaNode() - # Attach node to self - self.LightNode=parent.attachNewNode(node) + self.LightNode=parent.attachNewNode(light) self.LightNode.setTag("Metadata",tag) if(self.type=='spot'): self.LightNode.setHpr(self.orientation) @@ -418,8 +413,6 @@ class seLightManager(NodePath): ################################################################# type = lower(light.getType().getName()) - light.upcastToNamable() - specularColor = VBase4(1) position = Point3(0,0,0) orientation = Vec3(1,0,0) diff --git a/direct/src/showbase/ShadowDemo.py b/direct/src/showbase/ShadowDemo.py index daadd58c11..efcc491e71 100755 --- a/direct/src/showbase/ShadowDemo.py +++ b/direct/src/showbase/ShadowDemo.py @@ -201,13 +201,13 @@ def arbitraryShadow(node): ##b.reparentTo((base.localAvatar)) ##a = AmbientLight('cloudAmbientHi') ##a.setColor(Vec4(0.9, 0.9, 0.9, 1.000)) -##aNP = s.attachNewNode(a.upcastToPandaNode()) +##aNP = s.attachNewNode(a) ##b.setLight(aNP) ##d = DirectionalLight("chernabogDirectionalLight") ##d.setDirection(Vec3(0, 1, 0)) ##d.setColor(Vec4(1)) ###d.setColor(Vec4(0.9, 0.7, 0.7, 1.000)) -##dNP = s.attachNewNode(d.upcastToPandaNode()) +##dNP = s.attachNewNode(d) ##b.setLight(dNP) ## ##ival = Sequence(LerpPosInterval(bs.lightPath, 0.0, Vec3(-200, 0, 50)), diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 42ee315909..367ae57d1a 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -1553,8 +1553,7 @@ class ShowBase(DirectObject.DirectObject): # mouse activity. mw = self.buttonThrowers[0].getParent() mouseRecorder = MouseRecorder('mouse') - self.recorder.addRecorder( - 'mouse', mouseRecorder.upcastToRecorderBase()) + self.recorder.addRecorder('mouse', mouseRecorder) np = mw.getParent().attachNewNode(mouseRecorder) mw.reparentTo(np) From 2450f31ef541772613459733c66eab37d93a73e8 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 27 Feb 2018 15:06:12 +0100 Subject: [PATCH 113/118] interrogate: remove deprecated and unneeded downcastTo*() functions They have not been needed for a very long time, and they create an awkward reverse dependency of base classes on derived classes. --- dtool/src/interrogate/interfaceMakerPythonNative.cxx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 3a861a4844..96420ef663 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -3500,6 +3500,10 @@ write_function_for_name(ostream &out, Object *obj, out << " */\n"; + if (has_this && obj == nullptr) { + assert(obj != nullptr); + } + out << function_name << " {\n"; if (has_this) { @@ -7007,7 +7011,7 @@ record_object(TypeIndex type_index) { object->_methods.push_back(function); } } - if (itype.derivation_has_downcast(di)) { + /*if (itype.derivation_has_downcast(di)) { // Downcasts are methods of the base class, not the child class. TypeIndex base_type_index = itype.get_derivation(di); @@ -7020,7 +7024,7 @@ record_object(TypeIndex type_index) { pobject->_methods.push_back(function); } } - } + }*/ } } From fa231664bd34a13c9f2de1621331e832fb757d40 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 27 Feb 2018 18:47:51 +0100 Subject: [PATCH 114/118] interrogate: don't mark base types as global --- dtool/src/interrogate/interrogateBuilder.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx index 6cbf8f02c0..63d579c68f 100644 --- a/dtool/src/interrogate/interrogateBuilder.cxx +++ b/dtool/src/interrogate/interrogateBuilder.cxx @@ -2603,7 +2603,7 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, const CPPStructType::Base &base = (*bi); if (base._vis <= V_public) { CPPType *base_type = TypeManager::resolve_type(base._base, scope); - TypeIndex base_index = get_type(base_type, true); + TypeIndex base_index = get_type(base_type, false); if (base_index == 0) { if (base_type != NULL) { From 162b86c6d9430b027499bfbb43767cca05ff4b6e Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 27 Feb 2018 19:33:18 +0100 Subject: [PATCH 115/118] interrogatedb: fix library name setting for typedefs Typedefs are evidently always considered "fully defined", so we need to add another condition to the database merging code to prefer the type that is marked "global" when merging two types. --- dtool/src/interrogatedb/interrogateType.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dtool/src/interrogatedb/interrogateType.cxx b/dtool/src/interrogatedb/interrogateType.cxx index a977ff987a..a6d6e45487 100644 --- a/dtool/src/interrogatedb/interrogateType.cxx +++ b/dtool/src/interrogatedb/interrogateType.cxx @@ -112,14 +112,16 @@ operator = (const InterrogateType ©) { /** * Combines type with the other similar definition. If one type is "fully - * defined" and the other one isn't, the fully-defined type wins. + * defined" and the other one isn't, the fully-defined type wins. If both + * types are fully defined, whichever type is marked "global" wins. */ void InterrogateType:: merge_with(const InterrogateType &other) { // The only thing we care about copying from the non-fully-defined type // right now is the global flag. - if (is_fully_defined()) { + if (is_fully_defined() && + (!other.is_fully_defined() || (other._flags & F_global) == 0)) { // We win. _flags |= (other._flags & F_global); From bdb575e7668dba5d5e802b07c212edcca9f2244a Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 27 Feb 2018 19:44:14 +0100 Subject: [PATCH 116/118] interrogatedb: add interrogate_type_is_global (+regenerate pydtool) --- dtool/metalibs/dtoolconfig/pydtool.cxx | 76 ++++++++++++++++++- .../interrogatedb/interrogate_interface.cxx | 6 ++ .../src/interrogatedb/interrogate_interface.h | 1 + 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/dtool/metalibs/dtoolconfig/pydtool.cxx b/dtool/metalibs/dtoolconfig/pydtool.cxx index d43ca96f9a..b83fb05e11 100644 --- a/dtool/metalibs/dtoolconfig/pydtool.cxx +++ b/dtool/metalibs/dtoolconfig/pydtool.cxx @@ -1,6 +1,6 @@ /* * This file was generated by: - * interrogate -D EXPCL_DTOOLCONFIG= -nodb -python -promiscuous -I/home/rdb/panda3d-git/built/include -module panda3d.interrogatedb -library interrogatedb -string -true-names -do-module -oc pydtool.cxx ../../src/interrogatedb/interrogate_interface.h ../../src/interrogatedb/interrogate_request.h + * interrogate -D EXPCL_DTOOLCONFIG= -nodb -python -promiscuous -I../../../built/include -module panda3d.interrogatedb -library interrogatedb -string -true-names -do-module -oc pydtool.cxx ../../src/interrogatedb/interrogate_interface.h ../../src/interrogatedb/interrogate_request.h * */ @@ -15,7 +15,7 @@ #define PY_SSIZE_T_CLEAN 1 #if PYTHON_FRAMEWORK - #include "Python/Python.h" + #include #else #include "Python.h" #endif @@ -85,6 +85,9 @@ static PyObject *_inP07ytsqGH(PyObject *self, PyObject *args); static PyObject *_inP07yt7shV(PyObject *self, PyObject *args); static PyObject *_inP07ytA1eF(PyObject *self, PyObject *args); static PyObject *_inP07yt776V(PyObject *self, PyObject *args); +static PyObject *_inP07ytryup(PyObject *self, PyObject *args); +static PyObject *_inP07ytiytI(PyObject *self, PyObject *args); +static PyObject *_inP07ytZc07(PyObject *self, PyObject *args); static PyObject *_inP07ytfaH0(PyObject *self, PyObject *args); static PyObject *_inP07ytGB9D(PyObject *self, PyObject *args); static PyObject *_inP07ytsxxs(PyObject *self, PyObject *args); @@ -94,6 +97,7 @@ static PyObject *_inP07yt4Px8(PyObject *self, PyObject *args); static PyObject *_inP07ytNHcs(PyObject *self, PyObject *args); static PyObject *_inP07ytqHrb(PyObject *self, PyObject *args); static PyObject *_inP07ytaOqq(PyObject *self, PyObject *args); +static PyObject *_inP07ytpTBb(PyObject *self, PyObject *args); static PyObject *_inP07ytqWOw(PyObject *self, PyObject *args); static PyObject *_inP07ytHu7x(PyObject *self, PyObject *args); static PyObject *_inP07ytwGnA(PyObject *self, PyObject *args); @@ -1237,6 +1241,56 @@ _inP07yt776V(PyObject *, PyObject *args) { return (PyObject *)NULL; } +/* + * Python simple wrapper for + * char const *interrogate_make_seq_scoped_name(MakeSeqIndex make_seq) + */ +static PyObject * +_inP07ytryup(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_make_seq_scoped_name((MakeSeqIndex)param0); +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromString(return_value); +#else + return PyString_FromString(return_value); +#endif + } + return (PyObject *)NULL; +} + +/* + * Python simple wrapper for + * bool interrogate_make_seq_has_comment(ElementIndex element) + */ +static PyObject * +_inP07ytiytI(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_make_seq_has_comment((ElementIndex)param0); + return PyBool_FromLong(return_value); + } + return (PyObject *)NULL; +} + +/* + * Python simple wrapper for + * char const *interrogate_make_seq_comment(ElementIndex element) + */ +static PyObject * +_inP07ytZc07(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_make_seq_comment((ElementIndex)param0); +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromString(return_value); +#else + return PyString_FromString(return_value); +#endif + } + return (PyObject *)NULL; +} + /* * Python simple wrapper for * char const *interrogate_make_seq_num_name(MakeSeqIndex make_seq) @@ -1397,6 +1451,20 @@ _inP07ytaOqq(PyObject *, PyObject *args) { return (PyObject *)NULL; } +/* + * Python simple wrapper for + * bool interrogate_type_is_global(TypeIndex type) + */ +static PyObject * +_inP07ytpTBb(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_global((TypeIndex)param0); + return PyBool_FromLong(return_value); + } + return (PyObject *)NULL; +} + /* * Python simple wrapper for * char const *interrogate_type_name(TypeIndex type) @@ -2416,6 +2484,9 @@ static PyMethodDef python_simple_funcs[] = { { "interrogate_wrapper_unique_name", &_inP07yt7shV, METH_VARARGS }, { "interrogate_get_wrapper_by_unique_name", &_inP07ytA1eF, METH_VARARGS }, { "interrogate_make_seq_seq_name", &_inP07yt776V, METH_VARARGS }, + { "interrogate_make_seq_scoped_name", &_inP07ytryup, METH_VARARGS }, + { "interrogate_make_seq_has_comment", &_inP07ytiytI, METH_VARARGS }, + { "interrogate_make_seq_comment", &_inP07ytZc07, METH_VARARGS }, { "interrogate_make_seq_num_name", &_inP07ytfaH0, METH_VARARGS }, { "interrogate_make_seq_element_name", &_inP07ytGB9D, METH_VARARGS }, { "interrogate_number_of_global_types", &_inP07ytsxxs, METH_VARARGS }, @@ -2425,6 +2496,7 @@ static PyMethodDef python_simple_funcs[] = { { "interrogate_get_type_by_name", &_inP07ytNHcs, METH_VARARGS }, { "interrogate_get_type_by_scoped_name", &_inP07ytqHrb, METH_VARARGS }, { "interrogate_get_type_by_true_name", &_inP07ytaOqq, METH_VARARGS }, + { "interrogate_type_is_global", &_inP07ytpTBb, METH_VARARGS }, { "interrogate_type_name", &_inP07ytqWOw, METH_VARARGS }, { "interrogate_type_scoped_name", &_inP07ytHu7x, METH_VARARGS }, { "interrogate_type_true_name", &_inP07ytwGnA, METH_VARARGS }, diff --git a/dtool/src/interrogatedb/interrogate_interface.cxx b/dtool/src/interrogatedb/interrogate_interface.cxx index bd5493fc39..6e948134d3 100644 --- a/dtool/src/interrogatedb/interrogate_interface.cxx +++ b/dtool/src/interrogatedb/interrogate_interface.cxx @@ -509,6 +509,12 @@ interrogate_get_type_by_true_name(const char *type_name) { return InterrogateDatabase::get_ptr()->lookup_type_by_true_name(type_name); } +bool +interrogate_type_is_global(TypeIndex type) { + // cerr << "interrogate_type_is_global(" << type << ")\n"; + return InterrogateDatabase::get_ptr()->get_type(type).is_global(); +} + const char * interrogate_type_name(TypeIndex type) { // cerr << "interrogate_type_name(" << type << ")\n"; diff --git a/dtool/src/interrogatedb/interrogate_interface.h b/dtool/src/interrogatedb/interrogate_interface.h index 63e385d527..6c434d7833 100644 --- a/dtool/src/interrogatedb/interrogate_interface.h +++ b/dtool/src/interrogatedb/interrogate_interface.h @@ -367,6 +367,7 @@ EXPCL_INTERROGATEDB TypeIndex interrogate_get_type(int n); EXPCL_INTERROGATEDB TypeIndex interrogate_get_type_by_name(const char *type_name); EXPCL_INTERROGATEDB TypeIndex interrogate_get_type_by_scoped_name(const char *type_name); EXPCL_INTERROGATEDB TypeIndex interrogate_get_type_by_true_name(const char *type_name); +EXPCL_INTERROGATEDB bool interrogate_type_is_global(TypeIndex type); EXPCL_INTERROGATEDB const char *interrogate_type_name(TypeIndex type); EXPCL_INTERROGATEDB const char *interrogate_type_scoped_name(TypeIndex type); EXPCL_INTERROGATEDB const char *interrogate_type_true_name(TypeIndex type); From 8b6616814d52e305c7b9ae26aab1c071a0c9b6a6 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 27 Feb 2018 20:20:20 +0100 Subject: [PATCH 117/118] interrogate_module: warn about circular dependencies between classes This will help us catch cases wherein we inadvertently create a circular dependency between component libraries. Right now it only checks inheritance and typedefs, but this can be expanded in the future. Also, sorts the module initialization by dependency topology. This isn't very important right now, but having this guarantee might help when refactoring module initialization in the future. --- dtool/src/interrogate/interrogate_module.cxx | 174 ++++++++++++++++++- 1 file changed, 168 insertions(+), 6 deletions(-) diff --git a/dtool/src/interrogate/interrogate_module.cxx b/dtool/src/interrogate/interrogate_module.cxx index 5feae341b7..8c843e9da1 100644 --- a/dtool/src/interrogate/interrogate_module.cxx +++ b/dtool/src/interrogate/interrogate_module.cxx @@ -76,6 +76,79 @@ upcase_string(const string &str) { } */ +/** + * Finds a dependency cycle between the given dependency mapping, starting at + * the node that is already placed in the given cycle vector. + */ +static bool find_dependency_cycle(vector_string &cycle, map > &dependencies) { + assert(!cycle.empty()); + + const set &deps = dependencies[cycle.back()]; + for (auto it = deps.begin(); it != deps.end(); ++it) { + auto it2 = std::find(cycle.begin(), cycle.end(), *it); + if (it2 != cycle.end()) { + // Chop off the part of the chain that is not relevant. + cycle.erase(cycle.begin(), it2); + cycle.push_back(*it); + return true; + } + + // Recurse. + cycle.push_back(*it); + if (find_dependency_cycle(cycle, dependencies)) { + return true; + } + cycle.pop_back(); + } + + return false; +} + +/** + * Given that a direct link has been established between the two libraries, + * finds the two types that make up this relationship and prints out the + * nature of their dependency. + */ +static bool print_dependent_types(const string &lib1, const string &lib2) { + for (int ti = 0; ti < interrogate_number_of_global_types(); ti++) { + TypeIndex thetype = interrogate_get_global_type(ti); + if (interrogate_type_has_module_name(thetype) && + interrogate_type_has_library_name(thetype) && + lib1 == interrogate_type_library_name(thetype) && + module_name == interrogate_type_module_name(thetype)) { + + // Get the dependencies for this library. + int num_derivations = interrogate_type_number_of_derivations(thetype); + for (int di = 0; di < num_derivations; ++di) { + TypeIndex basetype = interrogate_type_get_derivation(thetype, di); + if (interrogate_type_is_global(basetype) && + interrogate_type_has_library_name(basetype) && + interrogate_type_library_name(basetype) == lib2) { + cerr + << " " << interrogate_type_scoped_name(thetype) << " (" + << lib1 << ") inherits from " + << interrogate_type_scoped_name(basetype) << " (" << lib2 << ")\n"; + return true; + } + } + + // It also counts if this is a typedef pointing to another type. + if (interrogate_type_is_typedef(thetype)) { + TypeIndex wrapped = interrogate_type_wrapped_type(thetype); + if (interrogate_type_is_global(wrapped) && + interrogate_type_has_library_name(wrapped) && + interrogate_type_library_name(wrapped) == lib2) { + cerr + << " " << interrogate_type_scoped_name(thetype) << " (" + << lib1 << ") is a typedef to " + << interrogate_type_scoped_name(wrapped) << " (" << lib2 << ")\n"; + } + } + } + } + return false; +} + int write_python_table_native(ostream &out) { out << "\n#include \"dtoolbase.h\"\n" << "#include \"interrogate_request.h\"\n\n" @@ -83,7 +156,7 @@ int write_python_table_native(ostream &out) { int count = 0; - vector_string libraries; + map > dependencies; // out << "extern \"C\" {\n"; @@ -99,23 +172,112 @@ int write_python_table_native(ostream &out) { // name add it to set of libraries if (interrogate_function_has_library_name(function_index)) { string library_name = interrogate_function_library_name(function_index); - if (std::find(libraries.begin(), libraries.end(), library_name) == libraries.end()) { - libraries.push_back(library_name); - } + dependencies[library_name]; } // } } - for (int ti = 0; ti < interrogate_number_of_types(); ti++) { - TypeIndex thetype = interrogate_get_type(ti); + for (int ti = 0; ti < interrogate_number_of_global_types(); ti++) { + TypeIndex thetype = interrogate_get_global_type(ti); if (interrogate_type_has_module_name(thetype) && module_name == interrogate_type_module_name(thetype)) { if (interrogate_type_has_library_name(thetype)) { string library_name = interrogate_type_library_name(thetype); + set &deps = dependencies[library_name]; + + // Get the dependencies for this library. + int num_derivations = interrogate_type_number_of_derivations(thetype); + for (int di = 0; di < num_derivations; ++di) { + TypeIndex basetype = interrogate_type_get_derivation(thetype, di); + if (interrogate_type_is_global(basetype) && + interrogate_type_has_library_name(basetype)) { + string baselib = interrogate_type_library_name(basetype); + if (baselib != library_name) { + deps.insert(move(baselib)); + } + } + } + + if (interrogate_type_is_typedef(thetype)) { + TypeIndex wrapped = interrogate_type_wrapped_type(thetype); + if (interrogate_type_is_global(wrapped) && + interrogate_type_has_library_name(wrapped)) { + string wrappedlib = interrogate_type_library_name(wrapped); + if (wrappedlib != library_name) { + deps.insert(move(wrappedlib)); + } + } + } + } + } + } + + // Now add the libraries in their proper ordering, based on dependencies. + vector_string libraries; + while (libraries.size() < dependencies.size()) { + // We have this check to make sure we don't enter an infinite loop. + bool added_any = false; + + for (auto it = dependencies.begin(); it != dependencies.end(); ++it) { + const string &library_name = it->first; + set &deps = dependencies[library_name]; + + // Remove the dependencies that have already been added from the deps. + if (!deps.empty()) { + for (auto li = libraries.begin(); li != libraries.end(); ++li) { + deps.erase(*li); + } + } + + if (deps.empty()) { + // OK, no remaining dependencies, so we can add this. if (std::find(libraries.begin(), libraries.end(), library_name) == libraries.end()) { libraries.push_back(library_name); + added_any = true; } } } + + if (!added_any) { + // Oh dear, we must have hit a circular dependency. Go through the + // remaining libraries to figure it out and print it. + cerr << "Circular dependency between libraries detected:\n"; + for (auto it = dependencies.begin(); it != dependencies.end(); ++it) { + const string &library_name = it->first; + set &deps = dependencies[library_name]; + if (deps.empty()) { + continue; + } + + // But since it does indicate a potential architectural flaw, we do + // want to let the user know about this. + vector_string cycle; + cycle.push_back(library_name); + if (!find_dependency_cycle(cycle, dependencies)) { + continue; + } + assert(cycle.size() >= 2); + + // Show the cycle of library dependencies. + auto ci = cycle.begin(); + cerr << " " << *ci; + for (++ci; ci != cycle.end(); ++ci) { + cerr << " -> " << *ci; + } + cerr << "\n"; + + // Now print out the actual types that make up the cycle. + ci = cycle.begin(); + string prev = *ci; + for (++ci; ci != cycle.end(); ++ci) { + print_dependent_types(prev, *ci); + prev = *ci; + } + + // We have to arbitrarily break one of the dependencies in order to be + // able to proceed. Break the first dependency. + dependencies[cycle[0]].erase(cycle[1]); + } + } } vector_string::const_iterator ii; From 361d36380d74306dcbea12f63403f19d1ff63215 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 27 Feb 2018 20:36:53 +0100 Subject: [PATCH 118/118] makepanda: don't put pview metadata in rpm when building without pview Fixes: #260 --- makepanda/makepanda.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 21c0181159..7a8926efd6 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -6937,16 +6937,19 @@ This package contains the SDK for development with Panda3D, install panda3d-runt /etc/Confauto.prc /etc/Config.prc /usr/share/panda3d -/usr/share/mime-info/panda3d.mime -/usr/share/mime-info/panda3d.keys -/usr/share/mime/packages/panda3d.xml -/usr/share/application-registry/panda3d.applications -/usr/share/applications/*.desktop /etc/ld.so.conf.d/panda3d.conf /usr/%_lib/panda3d """ + PYTHON_SITEPACKAGES + """ /usr/include/panda3d """ +if not PkgSkip("PVIEW"): + INSTALLER_SPEC_FILE += \ +"""/usr/share/applications/pview.desktop +/usr/share/mime-info/panda3d.mime +/usr/share/mime-info/panda3d.keys +/usr/share/mime/packages/panda3d.xml +/usr/share/application-registry/panda3d.applications +""" RUNTIME_INSTALLER_SPEC_FILE=""" Summary: Runtime binary and browser plugin for the Panda3D Game Engine