From 951c182c0e6a4372ef70530790b18e44b7a47f02 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 30 Nov 2020 19:46:06 +0100 Subject: [PATCH 1/6] makepanda: Preparatory changes for Big Sur / arm64 builds --- dtool/src/dtoolbase/dtool_platform.h | 2 ++ makepanda/makepanda.py | 28 ++++++++++++++++------------ makepanda/makepandacore.py | 12 +++++++----- makepanda/makewheel.py | 3 ++- 4 files changed, 27 insertions(+), 18 deletions(-) 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 a13c6ed840..d2e8c9f598 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -94,7 +94,7 @@ PkgListSet(["PYTHON", "DIRECT", # Python support "GTK2", # GTK2 is used for PStats on Unix "MFC", "WX", "FLTK", # Used for web plug-in only "ROCKET", # GUI libraries - "CARBON", "COCOA", # Mac OS X toolkits + "CARBON", "COCOA", # macOS toolkits "X11", # Unix platform support "PANDATOOL", "PVIEW", "DEPLOYTOOLS", # Toolchain "SKEL", # Example SKEL project @@ -141,8 +141,8 @@ def usage(problem): print(" --outputdir X (use the specified directory instead of 'built')") print(" --host URL (set the host url (runtime build only))") 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(" --universal (build universal binaries (OS X only))") + print(" --osxtarget N (the macOS version number to build for (macOS only))") + print(" --universal (build universal binaries (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))") @@ -448,6 +448,10 @@ elif target == 'darwin': arch_tag = 'fat64' elif frozenset(OSX_ARCHS) == frozenset(('x86_64', 'i386', 'ppc')): arch_tag = 'fat32' + elif frozenset(OSX_ARCHS) == frozenset(('x86_64', 'i386', 'ppc64', 'ppc')): + arch_tag = 'universal' + elif frozenset(OSX_ARCHS) == frozenset(('x86_64', 'arm64')): + arch_tag = 'universal2' else: raise RuntimeError('No arch tag for arch combination %s' % OSX_ARCHS) @@ -911,8 +915,8 @@ if (COMPILER=="GCC"): elif RUNTIME: # We don't support Cocoa in the runtime yet. PkgDisable("COCOA") - if 'x86_64' in OSX_ARCHS: - # 64-bits OS X doesn't have Carbon. + if 'x86_64' in OSX_ARCHS or 'arm64' in OSX_ARCHS: + # 64-bits macOS doesn't have Carbon. PkgDisable("CARBON") #if (PkgSkip("PYTHON")==0): @@ -1129,7 +1133,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")): @@ -1689,7 +1693,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__' @@ -1979,7 +1983,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: @@ -2285,7 +2289,7 @@ def Package(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 = [] @@ -6637,13 +6641,13 @@ for VER in MAYAVERSIONS: VNUM = VER[4:] if not PkgSkip(VER) and not PkgSkip("PANDATOOL") and not PkgSkip("EGG"): if GetTarget() == 'darwin' and int(VNUM) >= 2012: - ARCH_OPTS = ['NOARCH:PPC', 'NOARCH:I386'] + ARCH_OPTS = ['NOARCH:PPC', 'NOARCH:I386', 'NOARCH:ARM64'] if len(OSX_ARCHS) != 0 and 'x86_64' not in OSX_ARCHS: continue elif GetTarget() == 'darwin' and int(VNUM) >= 2009: - ARCH_OPTS = ['NOARCH:PPC'] + ARCH_OPTS = ['NOARCH:PPC', 'NOARCH:ARM64'] elif GetTarget() == 'darwin': - ARCH_OPTS = ['NOARCH:X86_64'] + ARCH_OPTS = ['NOARCH:X86_64', 'NOARCH:ARM64'] else: ARCH_OPTS = [] diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index ca4acfa700..de4b324bd5 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -46,7 +46,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. if sys.version_info >= (3, 0): @@ -377,11 +377,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: @@ -1315,7 +1317,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'): @@ -3114,7 +3116,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 4caebcc9e3..dbd559172c 100644 --- a/makepanda/makewheel.py +++ b/makepanda/makewheel.py @@ -387,7 +387,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' From 7b4ecbd60f9cb5372b730abc866f7360ec4ff38b Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Dec 2020 15:44:13 +0100 Subject: [PATCH 2/6] directtools: Fix use of built-in direct instead of base.direct --- direct/src/directtools/DirectSelection.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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) From 35eeb273860d344deb3503dab989496c6bb410ff Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Dec 2020 15:46:51 +0100 Subject: [PATCH 3/6] VFSImporter: fix detection of source file encoding in Python 3 Incidentally, this should (in theory) also fix the _bootlocale import loop referenced in #1065 --- direct/src/showbase/VFSImporter.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/direct/src/showbase/VFSImporter.py b/direct/src/showbase/VFSImporter.py index 7ed45db6bd..51f7eb75dd 100644 --- a/direct/src/showbase/VFSImporter.py +++ b/direct/src/showbase/VFSImporter.py @@ -65,7 +65,7 @@ class VFSImporter: vfile = vfs.getFile(filename, True) if vfile: return VFSLoader(dir_path, vfile, filename, - desc=('.py', 'r', imp.PY_SOURCE)) + desc=('.py', 'U' if sys.version_info < (3, 4) else 'r', imp.PY_SOURCE)) # If there's no .py file, but there's a .pyc file, load that # anyway. @@ -93,7 +93,7 @@ class VFSImporter: vfile = vfs.getFile(filename, True) if vfile: return VFSLoader(dir_path, vfile, filename, packagePath=path, - desc=('.py', 'r', imp.PY_SOURCE)) + desc=('.py', 'U' if sys.version_info < (3, 4) else 'r', imp.PY_SOURCE)) for ext in compiledExtensions: filename = Filename(path, '__init__.' + ext) vfile = vfs.getFile(filename, True) @@ -181,7 +181,15 @@ class VFSLoader: filename = Filename(self.filename) filename.setExtension('py') filename.setText() - return open(self.filename, self.desc[1]).read() + + if sys.version_info >= (3, 0): + # 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) + else: + return open(self.filename, self.desc[1]).read() def _import_extension_module(self, fullname): """ Loads the binary shared object as a Python module, and From 9525ddbfef326079e0f943cf773a742ec2a132a7 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Dec 2020 15:48:10 +0100 Subject: [PATCH 4/6] pgraph: LightAttrib::get_on_light(n) should call check_sorted() --- panda/src/pgraph/lightAttrib.I | 1 + 1 file changed, 1 insertion(+) 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]; } From 8e2c0dff4afceb6cb77303cb3e1463f3baddd1fb Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Dec 2020 15:51:36 +0100 Subject: [PATCH 5/6] pgraph: Hack fix for copying model with LightAttrib on root When a LightAttrib is set on the root of a model and the light is contained within that model, creating a copy (like the one Loader makes automatically) causes the LightAttrib to still point to the original light, not to the newly copied light. This works around it only for the case when the LightAttrib is set on the root, and only for the case of NodePath::copy_to() (it turns out there are edge cases when doing it in PandaNode::copy_subgraph() that would be hard to explain). See Moguri/blend2bam#44 --- panda/src/pgraph/lightAttrib.cxx | 60 ++++++++++++++++++++++++ panda/src/pgraph/lightAttrib.h | 2 + panda/src/pgraph/loader.cxx | 6 +-- panda/src/pgraph/nodePath.cxx | 79 +++++++++++++++++++++++++++++++- panda/src/pgraph/nodePath.h | 4 ++ 5 files changed, 146 insertions(+), 5 deletions(-) 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, From bf59e880c6c999ccb59bce26dd325bc109a054ed Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Dec 2020 16:25:29 +0100 Subject: [PATCH 6/6] bullet: Fix compilation error with Bullet 2.90+ --- panda/src/bullet/bulletSoftBodyNode.cxx | 8 ++++++++ 1 file changed, 8 insertions(+) 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()) {