diff --git a/direct/src/directtools/DirectSelection.py b/direct/src/directtools/DirectSelection.py index 49410656b3..3352705544 100644 --- a/direct/src/directtools/DirectSelection.py +++ b/direct/src/directtools/DirectSelection.py @@ -529,7 +529,7 @@ class SelectionQueue(CollisionHandlerQueue): # Well, no way to tell. Assume we're not backfacing. return 0 - if direct: + if base.direct: cam = base.direct.cam else: cam = base.cam @@ -602,7 +602,7 @@ class SelectionRay(SelectionQueue): if xy: mx = xy[0] my = xy[1] - elif direct: + elif base.direct: mx = base.direct.dr.mouseX my = base.direct.dr.mouseY else: @@ -613,7 +613,7 @@ class SelectionRay(SelectionQueue): mx = base.mouseWatcherNode.getMouseX() my = base.mouseWatcherNode.getMouseY() - if direct: + if base.direct: self.collider.setFromLens(base.direct.camNode, mx, my) else: self.collider.setFromLens(base.camNode, mx, my) diff --git a/direct/src/showbase/VFSImporter.py b/direct/src/showbase/VFSImporter.py index cc09d6de14..b96b6a2836 100644 --- a/direct/src/showbase/VFSImporter.py +++ b/direct/src/showbase/VFSImporter.py @@ -181,7 +181,12 @@ class VFSLoader: filename = Filename(self.filename) filename.setExtension('py') filename.setText() - return open(self.filename, self.desc[1]).read() + + # Use the tokenize module to detect the encoding. + import tokenize + fh = open(self.filename, 'rb') + encoding, lines = tokenize.detect_encoding(fh.readline) + return (b''.join(lines) + fh.read()).decode(encoding) def _import_extension_module(self, fullname): """ Loads the binary shared object as a Python module, and diff --git a/dtool/src/dtoolbase/dtool_platform.h b/dtool/src/dtoolbase/dtool_platform.h index 16ea54d88a..3f32e2d238 100644 --- a/dtool/src/dtoolbase/dtool_platform.h +++ b/dtool/src/dtoolbase/dtool_platform.h @@ -39,6 +39,8 @@ #define DTOOL_PLATFORM "osx_i386" #elif defined(__x86_64) #define DTOOL_PLATFORM "osx_amd64" +#elif defined(__aarch64__) +#define DTOOL_PLATFORM "osx_aarch64" #endif #elif defined(__FreeBSD__) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index ca6fb87929..b362d424f6 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -87,7 +87,7 @@ PkgListSet(["PYTHON", "DIRECT", # Python support "ARTOOLKIT", "OPENCV", "DIRECTCAM", "VISION", # Augmented Reality "GTK2", # GTK2 is used for PStats on Unix "MFC", "WX", "FLTK", # Used for web plug-in only - "COCOA", # Mac OS X toolkits + "COCOA", # macOS toolkits "X11", # Unix platform support "PANDATOOL", "PVIEW", "DEPLOYTOOLS", # Toolchain "SKEL", # Example SKEL project @@ -132,7 +132,7 @@ def usage(problem): print(" --distributor X (short string identifying the distributor of the build)") print(" --outputdir X (use the specified directory instead of 'built')") print(" --threads N (use the multithreaded build system. see manual)") - print(" --osxtarget N (the OS X version number to build for (OS X only))") + print(" --osxtarget N (the macOS version number to build for (macOS only))") print(" --override \"O=V\" (override dtool_config/prc option value)") print(" --static (builds libraries for static linking)") print(" --target X (experimental cross-compilation (android only))") @@ -939,7 +939,7 @@ if (COMPILER=="GCC"): if (PkgSkip(pkg)==0 and (pkg in SDK)): if (GetHost() == "darwin"): # Sheesh, Autodesk really can't make up their mind - # regarding the location of the Maya devkit on OS X. + # regarding the location of the Maya devkit on macOS. if (os.path.isdir(SDK[pkg] + "/Maya.app/Contents/lib")): LibDirectory(pkg, SDK[pkg] + "/Maya.app/Contents/lib") if (os.path.isdir(SDK[pkg] + "/Maya.app/Contents/MacOS")): @@ -1480,7 +1480,7 @@ def CompileIgate(woutd,wsrc,opts): target_arch = GetTargetArch() if target_arch in ("x86_64", "amd64"): cmd += ' -D_LP64' - elif target_arch == 'aarch64': + elif target_arch in ('aarch64', 'arm64'): cmd += ' -D_LP64 -D__LP64__ -D__aarch64__' else: cmd += ' -D__i386__' @@ -1766,7 +1766,7 @@ def CompileLink(dll, obj, opts): if (GetOrigExt(dll) == ".exe" and GetTarget() == 'windows' and "NOICON" not in opts): cmd += " " + GetOutputDir() + "/tmp/pandaIcon.res" - # Mac OS X specific flags. + # macOS specific flags. if GetTarget() == 'darwin': cmd += " -headerpad_max_install_names" if OSXTARGET is not None: @@ -2012,7 +2012,7 @@ def FreezePy(target, inputs, opts): ########################################################################################## def CompileBundle(target, inputs, opts): - assert GetTarget() == "darwin", 'bundles can only be made for Mac OS X' + assert GetTarget() == "darwin", 'bundles can only be made for macOS' plist = None resources = [] objects = [] diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 0592f1a4cf..304c88ab3a 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -40,7 +40,7 @@ DEBUG_DEPENDENCIES = False # Is the current Python a 32-bit or 64-bit build? There doesn't # appear to be a universal test for this. if sys.platform == 'darwin': - # On OSX, platform.architecture reports '64bit' even if it is + # On macOS, platform.architecture reports '64bit' even if it is # currently running in 32-bit mode. But sys.maxint is a reliable # indicator. host_64 = (sys.maxsize > 0x100000000) @@ -365,11 +365,13 @@ def SetTarget(target, arch=None): elif target == 'darwin': if arch == 'amd64': arch = 'x86_64' + if arch == 'aarch64': + arch = 'arm64' if arch is not None: - choices = ('i386', 'x86_64', 'ppc', 'ppc64') + choices = ('i386', 'x86_64', 'ppc', 'ppc64', 'arm64') if arch not in choices: - exit('Mac OS X architecture must be one of %s' % (', '.join(choices))) + exit('macOS architecture must be one of %s' % (', '.join(choices))) elif target == 'android' or target.startswith('android-'): if arch is None: @@ -1297,7 +1299,7 @@ def GetThirdpartyDir(): THIRDPARTYDIR = base + "/win-libs-vc" + vc + "/" elif (target == 'darwin'): - # OSX thirdparty binaries are universal, where possible. + # macOS thirdparty binaries are universal, where possible. THIRDPARTYDIR = base + "/darwin-libs-a/" elif (target == 'linux'): @@ -3002,7 +3004,7 @@ def SetupBuildEnvironment(compiler): dyldpath.insert(0, os.path.join(builtdir, 'lib')) os.environ["DYLD_LIBRARY_PATH"] = os.pathsep.join(dyldpath) - # OS X 10.11 removed DYLD_LIBRARY_PATH, but we still need to pass + # macOS 10.11 removed DYLD_LIBRARY_PATH, but we still need to pass # on our lib directory to ppackage, so add it to PATH instead. os.environ["PATH"] = os.path.join(builtdir, 'lib') + ':' + os.environ.get("PATH", "") diff --git a/makepanda/makewheel.py b/makepanda/makewheel.py index 3b2ff1697a..c841630bd8 100644 --- a/makepanda/makewheel.py +++ b/makepanda/makewheel.py @@ -381,7 +381,8 @@ class WheelFile(object): # On macOS, if no fat wheel was requested, extract the right architecture. if sys.platform == "darwin" and is_fat_file(source_path) \ and not self.platform.endswith("_intel") \ - and "_fat" not in self.platform: + and "_fat" not in self.platform \ + and "_universal" not in self.platform: if self.platform.endswith("_x86_64"): arch = 'x86_64' diff --git a/panda/src/bullet/bulletSoftBodyNode.cxx b/panda/src/bullet/bulletSoftBodyNode.cxx index f4ae8d2120..ea07728954 100644 --- a/panda/src/bullet/bulletSoftBodyNode.cxx +++ b/panda/src/bullet/bulletSoftBodyNode.cxx @@ -187,14 +187,22 @@ transform_changed() { // Offset between current approx center and current initial transform btVector3 pos = LVecBase3_to_btVector3(this->do_get_aabb().get_approx_center()); +#if BT_BULLET_VERSION >= 290 + btVector3 origin = _soft->getWorldTransform().getOrigin(); +#else btVector3 origin = _soft->m_initialWorldTransform.getOrigin(); +#endif btVector3 offset = pos - origin; // Subtract offset to get new transform for the body trans.setOrigin(trans.getOrigin() - offset); // Now apply the new transform +#if BT_BULLET_VERSION >= 290 + _soft->transform(_soft->getWorldTransform().inverse()); +#else _soft->transform(_soft->m_initialWorldTransform.inverse()); +#endif _soft->transform(trans); if (ts->has_scale()) { diff --git a/panda/src/pgraph/lightAttrib.I b/panda/src/pgraph/lightAttrib.I index 0c6686181b..bc02883c7e 100644 --- a/panda/src/pgraph/lightAttrib.I +++ b/panda/src/pgraph/lightAttrib.I @@ -42,6 +42,7 @@ get_num_non_ambient_lights() const { */ INLINE NodePath LightAttrib:: get_on_light(size_t n) const { + check_sorted(); nassertr(n < _sorted_on_lights.size(), NodePath::fail()); return _sorted_on_lights[n]; } diff --git a/panda/src/pgraph/lightAttrib.cxx b/panda/src/pgraph/lightAttrib.cxx index 9bc99674f8..9820f1bcfa 100644 --- a/panda/src/pgraph/lightAttrib.cxx +++ b/panda/src/pgraph/lightAttrib.cxx @@ -443,6 +443,36 @@ remove_on_light(const NodePath &light) const { return return_new(attrib); } +/** + * Returns a new LightAttrib, just like this one, but with the indicated light + * replaced with the given other light. + */ +CPT(RenderAttrib) LightAttrib:: +replace_on_light(const NodePath &source, const NodePath &dest) const { + if (source == dest) { + return this; + } + + nassertr(!source.is_empty(), this); + Light *slobj = source.node()->as_light(); + nassertr(slobj != nullptr, this); + + nassertr(!dest.is_empty(), this); + Light *dlobj = dest.node()->as_light(); + nassertr(dlobj != nullptr, this); + + LightAttrib *attrib = new LightAttrib(*this); + + auto it = attrib->_on_lights.find(source); + if (it != attrib->_on_lights.end()) { + dlobj->attrib_ref(); + slobj->attrib_unref(); + + *it = dest; + } + return return_new(attrib); +} + /** * Returns a new LightAttrib, just like this one, but with the indicated light * added to the list of lights turned off by this attrib. @@ -475,6 +505,36 @@ remove_off_light(const NodePath &light) const { return return_new(attrib); } +/** + * Returns a new LightAttrib, just like this one, but with the indicated light + * replaced with the given other light. + */ +CPT(RenderAttrib) LightAttrib:: +replace_off_light(const NodePath &source, const NodePath &dest) const { + if (source == dest) { + return this; + } + + nassertr(!source.is_empty(), this); + Light *slobj = source.node()->as_light(); + nassertr(slobj != nullptr, this); + + nassertr(!dest.is_empty(), this); + Light *dlobj = dest.node()->as_light(); + nassertr(dlobj != nullptr, this); + + LightAttrib *attrib = new LightAttrib(*this); + + auto it = attrib->_off_lights.find(source); + if (it != attrib->_off_lights.end()) { + dlobj->attrib_ref(); + slobj->attrib_unref(); + + *it = dest; + } + return return_new(attrib); +} + /** * Returns the most important light (that is, the light with the highest * priority) in the LightAttrib, excluding any ambient lights. Returns an diff --git a/panda/src/pgraph/lightAttrib.h b/panda/src/pgraph/lightAttrib.h index 4872ebc623..361a4a6c1e 100644 --- a/panda/src/pgraph/lightAttrib.h +++ b/panda/src/pgraph/lightAttrib.h @@ -85,8 +85,10 @@ PUBLISHED: CPT(RenderAttrib) add_on_light(const NodePath &light) const; CPT(RenderAttrib) remove_on_light(const NodePath &light) const; + CPT(RenderAttrib) replace_on_light(const NodePath &source, const NodePath &dest) const; CPT(RenderAttrib) add_off_light(const NodePath &light) const; CPT(RenderAttrib) remove_off_light(const NodePath &light) const; + CPT(RenderAttrib) replace_off_light(const NodePath &source, const NodePath &dest) const; NodePath get_most_important_light() const; LColor get_ambient_contribution() const; diff --git a/panda/src/pgraph/loader.cxx b/panda/src/pgraph/loader.cxx index ab4db6ea2f..15b130592d 100644 --- a/panda/src/pgraph/loader.cxx +++ b/panda/src/pgraph/loader.cxx @@ -292,7 +292,7 @@ try_load_file(const Filename &pathname, const LoaderOptions &options, << "Model " << pathname << " found in ModelPool.\n"; } // But return a deep copy of the shared model. - node = node->copy_subgraph(); + node = NodePath(node).copy_to(NodePath()).node(); } return node; } @@ -329,7 +329,7 @@ try_load_file(const Filename &pathname, const LoaderOptions &options, // from the RAM cached version. ModelPool::add_model(pathname, model_root); if ((options.get_flags() & LoaderOptions::LF_allow_instance) == 0) { - return model_root->copy_subgraph(); + return NodePath(model_root).copy_to(NodePath()).node(); } } } @@ -398,7 +398,7 @@ try_load_file(const Filename &pathname, const LoaderOptions &options, // cached version. ModelPool::add_model(pathname, DCAST(ModelRoot, result.p())); if ((options.get_flags() & LoaderOptions::LF_allow_instance) == 0) { - result = result->copy_subgraph(); + result = NodePath(result).copy_to(NodePath()).node(); } } diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index 1748942679..f955b9c380 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -539,12 +539,45 @@ copy_to(const NodePath &other, int sort, Thread *current_thread) const { nassertr(other._error_type == ET_ok, fail()); PandaNode *source_node = node(); - PT(PandaNode) copy_node = source_node->copy_subgraph(current_thread); + PandaNode::InstanceMap inst_map; + PT(PandaNode) copy_node = source_node->r_copy_subgraph(inst_map, current_thread); nassertr(copy_node != nullptr, fail()); copy_node->reset_prev_transform(current_thread); - return other.attach_new_node(copy_node, sort, current_thread); + NodePath result = other.attach_new_node(copy_node, sort, current_thread); + + // Temporary hack fix: if this root NodePath had lights applied that are + // located inside this subgraph, we need to fix them. + const RenderState *state = source_node->get_state(); + const LightAttrib *lattr; + if (state->get_attrib(lattr)) { + CPT(LightAttrib) new_lattr = lattr; + + for (size_t i = 0; i < lattr->get_num_off_lights(); ++i) { + NodePath light = lattr->get_off_light(i); + NodePath light2 = light; + + if (light2.replace_copied_nodes(*this, result, inst_map, current_thread)) { + new_lattr = DCAST(LightAttrib, new_lattr->replace_off_light(light, light2)); + } + } + + for (size_t i = 0; i < lattr->get_num_on_lights(); ++i) { + NodePath light = lattr->get_on_light(i); + NodePath light2 = light; + + if (light2.replace_copied_nodes(*this, result, inst_map, current_thread)) { + new_lattr = DCAST(LightAttrib, new_lattr->replace_on_light(light, light2)); + } + } + + if (new_lattr != lattr) { + result.set_state(state->set_attrib(std::move(new_lattr))); + } + } + + return result; } /** @@ -5803,6 +5836,48 @@ decode_from_bam_stream(vector_uchar data, BamReader *reader) { return result; } +/** + * If the given root node is an ancestor of this NodePath, replaces all + * components below it using the given instance map. + * + * This is a helper method used by copy_to(). + */ +bool NodePath:: +replace_copied_nodes(const NodePath &source, const NodePath &dest, + const PandaNode::InstanceMap &inst_map, + Thread *current_thread) { + nassertr(!dest.is_empty(), false); + + int pipeline_stage = current_thread->get_pipeline_stage(); + + pvector nodes; + + NodePathComponent *comp = _head; + while (comp != nullptr && comp != source._head) { + nodes.push_back(comp->get_node()); + + comp = comp->get_next(pipeline_stage, current_thread); + } + + if (comp == nullptr) { + // The given source NodePath isn't an ancestor of this NodePath. + return false; + } + + // Start at the dest NodePath and compose the new NodePath. + PT(NodePathComponent) new_comp = dest._head; + pvector::reverse_iterator it; + for (it = nodes.rbegin(); it != nodes.rend(); ++it) { + PandaNode::InstanceMap::const_iterator iit = inst_map.find(*it); + nassertr_always(iit != inst_map.end(), false); + new_comp = PandaNode::get_component(new_comp, iit->second, pipeline_stage, current_thread); + } + + nassertr(new_comp != nullptr, false); + _head = std::move(new_comp); + return true; +} + /** * Walks up from both NodePaths to find the first node that both have in * common, if any. Fills a_count and b_count with the number of nodes below diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index eaec935a29..187129d30c 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -952,6 +952,10 @@ PUBLISHED: static NodePath decode_from_bam_stream(vector_uchar data, BamReader *reader = nullptr); private: + bool replace_copied_nodes(const NodePath &source, const NodePath &dest, + const PandaNode::InstanceMap &inst_map, + Thread *current_thread); + static NodePathComponent * find_common_ancestor(const NodePath &a, const NodePath &b, int &a_count, int &b_count,