Merge branch 'master' into deploy-ng
This commit is contained in:
commit
aebdefcf60
|
|
@ -7,7 +7,7 @@ from direct.distributed.DoCollectionManager import DoCollectionManager
|
|||
from direct.showbase import GarbageReport
|
||||
from .PyDatagramIterator import PyDatagramIterator
|
||||
|
||||
import types
|
||||
import inspect
|
||||
import gc
|
||||
|
||||
__all__ = ["ConnectionRepository", "GCTrigger"]
|
||||
|
|
@ -327,13 +327,13 @@ class ConnectionRepository(
|
|||
if classDef is None:
|
||||
self.notify.debug("No class definition for %s." % (className))
|
||||
else:
|
||||
if type(classDef) == types.ModuleType:
|
||||
if inspect.ismodule(classDef):
|
||||
if not hasattr(classDef, className):
|
||||
self.notify.warning("Module %s does not define class %s." % (className, className))
|
||||
continue
|
||||
classDef = getattr(classDef, className)
|
||||
|
||||
if type(classDef) != types.ClassType and type(classDef) != types.TypeType:
|
||||
if inspect.isclass(classDef):
|
||||
self.notify.error("Symbol %s is not a class name." % (className))
|
||||
else:
|
||||
dclass.setClassDef(classDef)
|
||||
|
|
@ -388,7 +388,7 @@ class ConnectionRepository(
|
|||
if classDef is None:
|
||||
self.notify.error("No class definition for %s." % className)
|
||||
else:
|
||||
if type(classDef) == types.ModuleType:
|
||||
if inspect.ismodule(classDef):
|
||||
if not hasattr(classDef, className):
|
||||
self.notify.error("Module %s does not define class %s." % (className, className))
|
||||
classDef = getattr(classDef, className)
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ class PyDatagramIterator(DatagramIterator):
|
|||
b = self.getUint8()
|
||||
retVal.append((a, b))
|
||||
else:
|
||||
raise Exception("Error: No such type as: " + str(subAtomicType))
|
||||
raise Exception("Error: No such type as: " + str(subatomicType))
|
||||
else:
|
||||
# See if it is in the handy dict
|
||||
getFunc = self.FuncDict.get(subatomicType)
|
||||
|
|
@ -121,8 +121,4 @@ class PyDatagramIterator(DatagramIterator):
|
|||
else:
|
||||
raise Exception("Error: No such type as: " + str(subatomicType))
|
||||
|
||||
|
||||
|
||||
return retVal
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ from direct.task import Task
|
|||
from direct.directnotify import DirectNotifyGlobal
|
||||
from direct.distributed.PyDatagram import PyDatagram
|
||||
|
||||
import inspect
|
||||
|
||||
|
||||
class ServerRepository:
|
||||
|
||||
|
|
@ -273,12 +275,12 @@ class ServerRepository:
|
|||
if classDef == None:
|
||||
self.notify.debug("No class definition for %s." % (className))
|
||||
else:
|
||||
if type(classDef) == types.ModuleType:
|
||||
if inspect.ismodule(classDef):
|
||||
if not hasattr(classDef, className):
|
||||
self.notify.error("Module %s does not define class %s." % (className, className))
|
||||
classDef = getattr(classDef, className)
|
||||
|
||||
if type(classDef) != types.ClassType and type(classDef) != types.TypeType:
|
||||
if inspect.isclass(classDef):
|
||||
self.notify.error("Symbol %s is not a class name." % (className))
|
||||
else:
|
||||
dclass.setClassDef(classDef)
|
||||
|
|
|
|||
|
|
@ -342,7 +342,6 @@ class MetaInterval(CMetaInterval):
|
|||
# with all of their associated Python callbacks:
|
||||
|
||||
def setManager(self, manager):
|
||||
rogerroger
|
||||
self.__manager = manager
|
||||
CMetaInterval.setManager(self, manager)
|
||||
|
||||
|
|
|
|||
|
|
@ -110,15 +110,6 @@ class ObjectPool:
|
|||
print('TYPE: %s, %s objects' % (repr(typ), len(self._type2objs[typ])))
|
||||
print(getNumberedTypedSortedString(self._type2objs[typ]))
|
||||
|
||||
def containerLenStr(self):
|
||||
s = 'Object Pool: Container Lengths'
|
||||
s += '\n=============================='
|
||||
lengths = list(self._len2obj.keys())
|
||||
lengths.sort()
|
||||
lengths.reverse()
|
||||
for count in counts:
|
||||
pass
|
||||
|
||||
def printReferrers(self, numEach=3):
|
||||
"""referrers of the first few of each type of object"""
|
||||
counts = list(set(self._count2types.keys()))
|
||||
|
|
|
|||
|
|
@ -12,5 +12,28 @@ else:
|
|||
if '_Pmw' in sys.modules:
|
||||
sys.modules['_Pmw'].__name__ = '_Pmw'
|
||||
|
||||
# Hack to workaround broken Pmw.NoteBook in Python 3
|
||||
def bordercolors(root, colorName):
|
||||
lightRGB = []
|
||||
darkRGB = []
|
||||
for value in Pmw.Color.name2rgb(root, colorName, 1):
|
||||
value40pc = (14 * value) // 10
|
||||
if value40pc > int(Pmw.Color._MAX_RGB):
|
||||
value40pc = int(Pmw.Color._MAX_RGB)
|
||||
valueHalfWhite = (int(Pmw.Color._MAX_RGB) + value) // 2;
|
||||
lightRGB.append(max(value40pc, valueHalfWhite))
|
||||
|
||||
darkValue = (60 * value) // 100
|
||||
darkRGB.append(darkValue)
|
||||
|
||||
return (
|
||||
'#%04x%04x%04x' % (lightRGB[0], lightRGB[1], lightRGB[2]),
|
||||
'#%04x%04x%04x' % (darkRGB[0], darkRGB[1], darkRGB[2])
|
||||
)
|
||||
|
||||
Pmw.Color.bordercolors = bordercolors
|
||||
del bordercolors
|
||||
|
||||
|
||||
def spawnTkLoop():
|
||||
base.spawnTkLoop()
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ class BoundedSemaphore(Semaphore):
|
|||
Semaphore.__init__(value)
|
||||
|
||||
def release(self):
|
||||
if self.getCount() > value:
|
||||
if self.getCount() > self.__max:
|
||||
raise ValueError
|
||||
|
||||
Semaphore.release(self)
|
||||
|
|
|
|||
|
|
@ -67,8 +67,6 @@
|
|||
can define all of these stupid symbols to the empty string.
|
||||
*/
|
||||
|
||||
#define EXPCL_EMPTY
|
||||
|
||||
#ifdef BUILDING_DTOOL
|
||||
#define EXPCL_DTOOL EXPORT_CLASS
|
||||
#define EXPTP_DTOOL EXPORT_TEMPL
|
||||
|
|
|
|||
|
|
@ -194,9 +194,9 @@ output(ostream &out) const {
|
|||
/**
|
||||
* Returns a special zero-valued TypeHandle that is used to indicate no type.
|
||||
*/
|
||||
INLINE TypeHandle TypeHandle::
|
||||
CONSTEXPR TypeHandle TypeHandle::
|
||||
none() {
|
||||
return _none;
|
||||
return TypeHandle(0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -213,9 +213,15 @@ operator bool () const {
|
|||
*
|
||||
* See TypeRegistry::find_type_by_id().
|
||||
*/
|
||||
INLINE TypeHandle TypeHandle::
|
||||
CONSTEXPR TypeHandle TypeHandle::
|
||||
from_index(int index) {
|
||||
TypeHandle handle;
|
||||
handle._index = index;
|
||||
return handle;
|
||||
return TypeHandle(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor for initializing a TypeHandle from an index, used by
|
||||
* none() and by from_index().
|
||||
*/
|
||||
CONSTEXPR TypeHandle::
|
||||
TypeHandle(int index) : _index(index) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,8 @@ class TypedObject;
|
|||
*/
|
||||
class EXPCL_DTOOL TypeHandle FINAL {
|
||||
PUBLISHED:
|
||||
TypeHandle() NOEXCEPT DEFAULT_CTOR;
|
||||
|
||||
enum MemoryClass {
|
||||
MC_singleton,
|
||||
MC_array,
|
||||
|
|
@ -127,7 +129,7 @@ PUBLISHED:
|
|||
|
||||
INLINE int get_index() const;
|
||||
INLINE void output(ostream &out) const;
|
||||
INLINE static TypeHandle none();
|
||||
CONSTEXPR static TypeHandle none();
|
||||
INLINE operator bool () const;
|
||||
|
||||
MAKE_PROPERTY(index, get_index);
|
||||
|
|
@ -140,12 +142,15 @@ public:
|
|||
void *reallocate_array(void *ptr, size_t size) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT);
|
||||
void deallocate_array(void *ptr);
|
||||
|
||||
INLINE static TypeHandle from_index(int index);
|
||||
CONSTEXPR static TypeHandle from_index(int index);
|
||||
|
||||
private:
|
||||
int _index;
|
||||
CONSTEXPR TypeHandle(int index);
|
||||
|
||||
// Only kept temporarily for ABI compatibility.
|
||||
static TypeHandle _none;
|
||||
|
||||
int _index;
|
||||
friend class TypeRegistry;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,108 +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 newheader.cxx
|
||||
* @author drose
|
||||
* @date 2004-07-05
|
||||
*/
|
||||
|
||||
#include "dtoolbase.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
const char *cxx_style =
|
||||
"// Filename: %s\n"
|
||||
"// Created by: %s (%s)\n"
|
||||
"//\n"
|
||||
"////////////////////////////////////////////////////////////////////\n"
|
||||
"//\n"
|
||||
"// PANDA 3D SOFTWARE\n"
|
||||
"// Copyright (c) Carnegie Mellon University. All rights reserved.\n"
|
||||
"//\n"
|
||||
"// All use of this software is subject to the terms of the revised BSD\n"
|
||||
"// license. You should have received a copy of this license along\n"
|
||||
"// with this source code in a file named \"LICENSE.\"\n"
|
||||
"//\n"
|
||||
"////////////////////////////////////////////////////////////////////\n"
|
||||
"\n";
|
||||
|
||||
const char *c_style =
|
||||
"/* Filename: %s\n"
|
||||
" * Created by: %s (%s)\n"
|
||||
" *\n"
|
||||
" * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n"
|
||||
" *\n"
|
||||
" * PANDA 3D SOFTWARE\n"
|
||||
" * Copyright (c) Carnegie Mellon University. All rights reserved.\n"
|
||||
" *\n"
|
||||
" * All use of this software is subject to the terms of the revised BSD\n"
|
||||
" * license. You should have received a copy of this license along\n"
|
||||
" * with this source code in a file named \"LICENSE.\"\n"
|
||||
" *\n"
|
||||
" * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */\n"
|
||||
"\n";
|
||||
|
||||
struct FileDef {
|
||||
const char *extension;
|
||||
const char *header;
|
||||
};
|
||||
|
||||
FileDef file_def[] = {
|
||||
{ "h", cxx_style },
|
||||
{ "cxx", cxx_style },
|
||||
{ "I", cxx_style },
|
||||
{ "T", cxx_style },
|
||||
{ "c", c_style },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
void
|
||||
generate_header(const char *header, const string &filename) {
|
||||
const char *username = getenv("USER");
|
||||
if (username == NULL) {
|
||||
username = "";
|
||||
}
|
||||
|
||||
static const size_t max_date_buffer = 128;
|
||||
char date_buffer[max_date_buffer];
|
||||
time_t now = time(NULL);
|
||||
strftime(date_buffer, max_date_buffer, "%d%b%y", localtime(&now));
|
||||
|
||||
printf(header, filename.c_str(), username, date_buffer);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
if (argc < 2) {
|
||||
cerr << "Must specify the filename to generate a header for.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
string filename = argv[1];
|
||||
size_t dot = filename.rfind('.');
|
||||
if (dot == string::npos) {
|
||||
// No extension, no header.
|
||||
return 0;
|
||||
}
|
||||
|
||||
string extension = filename.substr(dot + 1);
|
||||
|
||||
size_t i = 0;
|
||||
while (file_def[i].extension != NULL) {
|
||||
if (extension == file_def[i].extension) {
|
||||
generate_header(file_def[i].header, filename);
|
||||
return 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// No matching extension, no problem.
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -5179,7 +5179,7 @@ if (PkgSkip("SPEEDTREE")==0):
|
|||
# DIRECTORY: panda/src/testbed/
|
||||
#
|
||||
|
||||
if (not RTDIST and not RUNTIME and PkgSkip("PVIEW")==0 and GetTarget() != 'android'):
|
||||
if (not RTDIST and not RUNTIME and PkgSkip("PVIEW")==0):
|
||||
OPTS=['DIR:panda/src/testbed']
|
||||
TargetAdd('pview_pview.obj', opts=OPTS, input='pview.cxx')
|
||||
TargetAdd('pview.exe', input='pview_pview.obj')
|
||||
|
|
@ -5198,6 +5198,7 @@ if (not RUNTIME and GetTarget() == 'android'):
|
|||
TargetAdd('org/panda3d/android/NativeIStream.class', opts=OPTS, input='NativeIStream.java')
|
||||
TargetAdd('org/panda3d/android/NativeOStream.class', opts=OPTS, input='NativeOStream.java')
|
||||
TargetAdd('org/panda3d/android/PandaActivity.class', opts=OPTS, input='PandaActivity.java')
|
||||
TargetAdd('org/panda3d/android/PythonActivity.class', opts=OPTS, input='PythonActivity.java')
|
||||
|
||||
TargetAdd('p3android_composite1.obj', opts=OPTS, input='p3android_composite1.cxx')
|
||||
TargetAdd('libp3android.dll', input='p3android_composite1.obj')
|
||||
|
|
@ -5208,10 +5209,10 @@ if (not RUNTIME and GetTarget() == 'android'):
|
|||
TargetAdd('android_main.obj', opts=OPTS, input='android_main.cxx')
|
||||
|
||||
if (not RTDIST and PkgSkip("PVIEW")==0):
|
||||
TargetAdd('pview_pview.obj', opts=OPTS, input='pview.cxx')
|
||||
TargetAdd('libpview_pview.obj', opts=OPTS, input='pview.cxx')
|
||||
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='libpview_pview.obj')
|
||||
TargetAdd('libpview.dll', input='libp3framework.dll')
|
||||
if not PkgSkip("EGG"):
|
||||
TargetAdd('libpview.dll', input='libpandaegg.dll')
|
||||
|
|
@ -5219,6 +5220,17 @@ if (not RUNTIME and GetTarget() == 'android'):
|
|||
TargetAdd('libpview.dll', input=COMMON_PANDA_LIBS)
|
||||
TargetAdd('libpview.dll', opts=['MODULE', 'ANDROID'])
|
||||
|
||||
if (not RTDIST and PkgSkip("PYTHON")==0):
|
||||
OPTS += ['PYTHON']
|
||||
TargetAdd('ppython_ppython.obj', opts=OPTS, input='python_main.cxx')
|
||||
TargetAdd('libppython.dll', input='android_native_app_glue.obj')
|
||||
TargetAdd('libppython.dll', input='android_main.obj')
|
||||
TargetAdd('libppython.dll', input='ppython_ppython.obj')
|
||||
TargetAdd('libppython.dll', input='libp3framework.dll')
|
||||
TargetAdd('libppython.dll', input='libp3android.dll')
|
||||
TargetAdd('libppython.dll', input=COMMON_PANDA_LIBS)
|
||||
TargetAdd('libppython.dll', opts=['MODULE', 'ANDROID', 'PYTHON'])
|
||||
|
||||
#
|
||||
# DIRECTORY: panda/src/androiddisplay/
|
||||
#
|
||||
|
|
@ -7632,7 +7644,7 @@ def MakeInstallerAndroid():
|
|||
continue
|
||||
if '.so.' in line:
|
||||
dep = line.rpartition('.so.')[0] + '.so'
|
||||
oscmd("patchelf --replace-needed %s %s %s" % (line, dep, target))
|
||||
oscmd("patchelf --replace-needed %s %s %s" % (line, dep, target), True)
|
||||
else:
|
||||
dep = line
|
||||
|
||||
|
|
@ -7643,6 +7655,7 @@ def MakeInstallerAndroid():
|
|||
copy_library(os.path.realpath(fulldep), dep)
|
||||
break
|
||||
|
||||
# Now copy every lib in the lib dir, and its dependencies.
|
||||
for base in os.listdir(source_dir):
|
||||
if not base.startswith('lib'):
|
||||
continue
|
||||
|
|
@ -7654,6 +7667,59 @@ def MakeInstallerAndroid():
|
|||
continue
|
||||
copy_library(source, base)
|
||||
|
||||
# Same for Python extension modules. However, Android is strict about
|
||||
# library naming, so we have a special naming scheme for these, in
|
||||
# conjunction with a custom import hook to find these modules.
|
||||
if not PkgSkip("PYTHON"):
|
||||
suffix = GetExtensionSuffix()
|
||||
source_dir = os.path.join(GetOutputDir(), "panda3d")
|
||||
for base in os.listdir(source_dir):
|
||||
if not base.endswith(suffix):
|
||||
continue
|
||||
modname = base[:-len(suffix)]
|
||||
source = os.path.join(source_dir, base)
|
||||
copy_library(source, "libpy.panda3d.{}.so".format(modname))
|
||||
|
||||
# Same for standard Python modules.
|
||||
import _ctypes
|
||||
source_dir = os.path.dirname(_ctypes.__file__)
|
||||
for base in os.listdir(source_dir):
|
||||
if not base.endswith('.so'):
|
||||
continue
|
||||
modname = base.partition('.')[0]
|
||||
source = os.path.join(source_dir, base)
|
||||
copy_library(source, "libpy.{}.so".format(modname))
|
||||
|
||||
def copy_python_tree(source_root, target_root):
|
||||
for source_dir, dirs, files in os.walk(source_root):
|
||||
if 'site-packages' in dirs:
|
||||
dirs.remove('site-packages')
|
||||
|
||||
if not any(base.endswith('.py') for base in files):
|
||||
continue
|
||||
|
||||
target_dir = os.path.join(target_root, os.path.relpath(source_dir, source_root))
|
||||
target_dir = os.path.normpath(target_dir)
|
||||
os.makedirs(target_dir, 0o755)
|
||||
|
||||
for base in files:
|
||||
if base.endswith('.py'):
|
||||
target = os.path.join(target_dir, base)
|
||||
shutil.copy(os.path.join(source_dir, base), target)
|
||||
|
||||
# Copy the Python standard library to the .apk as well.
|
||||
from distutils.sysconfig import get_python_lib
|
||||
stdlib_source = get_python_lib(False, True)
|
||||
stdlib_target = os.path.join("apkroot", "lib", "python{0}.{1}".format(*sys.version_info))
|
||||
copy_python_tree(stdlib_source, stdlib_target)
|
||||
|
||||
# But also copy over our custom site.py.
|
||||
shutil.copy("panda/src/android/site.py", os.path.join(stdlib_target, "site.py"))
|
||||
|
||||
# And now make a site-packages directory containing our direct/panda3d/pandac modules.
|
||||
for tree in "panda3d", "direct", "pandac":
|
||||
copy_python_tree(os.path.join(GetOutputDir(), tree), os.path.join(stdlib_target, "site-packages", tree))
|
||||
|
||||
# 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")))
|
||||
|
|
@ -7672,7 +7738,11 @@ def MakeInstallerAndroid():
|
|||
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"]))
|
||||
oscmd("cd apkroot && aapt add ../%s classes.dex" % (apk_unaligned))
|
||||
for path, dirs, files in os.walk('apkroot/lib'):
|
||||
if files:
|
||||
rel = os.path.relpath(path, 'apkroot')
|
||||
oscmd("cd apkroot && aapt add ../%s %s/*" % (apk_unaligned, rel))
|
||||
|
||||
# Now align the .apk, which is necessary for Android to load it.
|
||||
oscmd("zipalign -v -p 4 %s %s" % (apk_unaligned, apk_unsigned))
|
||||
|
|
|
|||
|
|
@ -87,9 +87,9 @@ public class PandaActivity extends NativeActivity {
|
|||
return path;
|
||||
}
|
||||
|
||||
public String getIntentOutputPath() {
|
||||
public String getIntentOutputUri() {
|
||||
Intent intent = getIntent();
|
||||
return intent.getStringExtra("org.panda3d.OUTPUT_PATH");
|
||||
return intent.getStringExtra("org.panda3d.OUTPUT_URI");
|
||||
}
|
||||
|
||||
public String getCacheDirString() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* 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 PythonActivity.java
|
||||
* @author rdb
|
||||
* @date 2018-02-04
|
||||
*/
|
||||
|
||||
package org.panda3d.android;
|
||||
|
||||
import org.panda3d.android.PandaActivity;
|
||||
|
||||
/**
|
||||
* This is only declared as a separate class from PandaActivity so that we
|
||||
* can have two separate activity definitions in ApplicationManifest.xml.
|
||||
*/
|
||||
public class PythonActivity extends PandaActivity {
|
||||
}
|
||||
|
|
@ -17,11 +17,14 @@
|
|||
#include "virtualFileSystem.h"
|
||||
#include "filename.h"
|
||||
#include "thread.h"
|
||||
#include "urlSpec.h"
|
||||
|
||||
#include "config_display.h"
|
||||
// #define OPENGLES_1 #include "config_androiddisplay.h"
|
||||
|
||||
#include <android_native_app_glue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
// struct android_app* panda_android_app = NULL;
|
||||
|
||||
|
|
@ -67,6 +70,55 @@ void android_main(struct android_app* app) {
|
|||
android_cat.info()
|
||||
<< "New native activity started on " << *current_thread << "\n";
|
||||
|
||||
// Were we given an optional location to write the stdout/stderr streams?
|
||||
methodID = env->GetMethodID(activity_class, "getIntentOutputUri", "()Ljava/lang/String;");
|
||||
jstring joutput_uri = (jstring) env->CallObjectMethod(activity->clazz, methodID);
|
||||
if (joutput_uri != nullptr) {
|
||||
const char *output_uri = env->GetStringUTFChars(joutput_uri, nullptr);
|
||||
|
||||
if (output_uri != nullptr && output_uri[0] != 0) {
|
||||
URLSpec spec(output_uri);
|
||||
|
||||
if (spec.get_scheme() == "file") {
|
||||
string path = spec.get_path();
|
||||
int fd = open(path.c_str(), O_CREAT | O_TRUNC | O_WRONLY);
|
||||
if (fd != -1) {
|
||||
android_cat.info()
|
||||
<< "Writing standard output to file " << path << "\n";
|
||||
|
||||
dup2(fd, 1);
|
||||
dup2(fd, 2);
|
||||
} else {
|
||||
android_cat.error()
|
||||
<< "Failed to open output path " << path << "\n";
|
||||
}
|
||||
} else if (spec.get_scheme() == "tcp") {
|
||||
string host = spec.get_server();
|
||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
struct sockaddr_in serv_addr = {0};
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_port = htons(spec.get_port());
|
||||
serv_addr.sin_addr.s_addr = inet_addr(host.c_str());
|
||||
if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == 0) {
|
||||
android_cat.info()
|
||||
<< "Writing standard output to socket "
|
||||
<< spec.get_server_and_port() << "\n";
|
||||
dup2(fd, 1);
|
||||
dup2(fd, 2);
|
||||
} else {
|
||||
android_cat.error()
|
||||
<< "Failed to open output socket "
|
||||
<< spec.get_server_and_port() << "\n";
|
||||
}
|
||||
close(fd);
|
||||
} else {
|
||||
android_cat.error()
|
||||
<< "Unsupported scheme in output URI: " << output_uri << "\n";
|
||||
}
|
||||
env->ReleaseStringUTFChars(joutput_uri, output_uri);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the data directory.
|
||||
jmethodID get_appinfo = env->GetMethodID(activity_class, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
|
||||
|
||||
|
|
@ -186,28 +238,6 @@ 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;
|
||||
|
|
@ -266,6 +296,9 @@ void android_main(struct android_app* app) {
|
|||
env->ReleaseStringUTFChars(filename, filename_str);
|
||||
}
|
||||
|
||||
close(1);
|
||||
close(2);
|
||||
|
||||
// Detach the thread before exiting.
|
||||
activity->vm->DetachCurrentThread();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-sdk android:minSdkVersion="21" />
|
||||
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
|
||||
|
||||
|
|
@ -44,13 +45,38 @@
|
|||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="*/*" scheme="content" host="com.termux.files" />
|
||||
<data android:pathPattern=".*\\.egg" />
|
||||
<data android:pathPattern=".*\\.egg.pz" />
|
||||
<data android:pathPattern=".*\\.egg.gz" />
|
||||
<data android:pathPattern=".*\\.bam" />
|
||||
<data android:pathPattern=".*\\.bam.pz" />
|
||||
<data android:pathPattern=".*\\.bam.gz" />
|
||||
<data android:mimeType="*/*" android:scheme="content" android:host="com.termux.files" android:pathPattern=".*\\.egg" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="org.panda3d.android.PythonActivity"
|
||||
android:label="Panda Python" android:theme="@android:style/Theme.NoTitleBar"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:launchMode="singleInstance">
|
||||
|
||||
<meta-data android:name="android.app.lib_name"
|
||||
android:value="ppython" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="text/x-python" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="*/*" android:scheme="file" />
|
||||
<data android:pathPattern=".*\\.py" />
|
||||
<data android:pathPattern=".*\\.pyw" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="*/*" android:scheme="content" android:host="com.termux.files" />
|
||||
<data android:pathPattern=".*\\.py" />
|
||||
<data android:pathPattern=".*\\.pyw" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* 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 python_main.cxx
|
||||
* @author rdb
|
||||
* @date 2018-02-12
|
||||
*/
|
||||
|
||||
#include "dtoolbase.h"
|
||||
#include "config_android.h"
|
||||
#include "executionEnvironment.h"
|
||||
|
||||
#undef _POSIX_C_SOURCE
|
||||
#undef _XOPEN_SOURCE
|
||||
#include <Python.h>
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
#include <wchar.h>
|
||||
#endif
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
/**
|
||||
* The main entry point for the Python activity. Called by android_main.
|
||||
*/
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc <= 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Help out Python by telling it which encoding to use
|
||||
Py_FileSystemDefaultEncoding = "utf-8";
|
||||
|
||||
Py_SetProgramName(Py_DecodeLocale("ppython", nullptr));
|
||||
|
||||
// Set PYTHONHOME to the location of the .apk file.
|
||||
string apk_path = ExecutionEnvironment::get_binary_name();
|
||||
Py_SetPythonHome(Py_DecodeLocale(apk_path.c_str(), nullptr));
|
||||
|
||||
// We need to make zlib available to zipimport, but I don't know how
|
||||
// we could inject our import hook before Py_Initialize, so instead
|
||||
// load it as though it were a built-in module.
|
||||
void *zlib = dlopen("libpy.zlib.so", RTLD_NOW);
|
||||
if (zlib != nullptr) {
|
||||
void *init = dlsym(zlib, "PyInit_zlib");
|
||||
if (init != nullptr) {
|
||||
PyImport_AppendInittab("zlib", (PyObject *(*)())init);
|
||||
}
|
||||
}
|
||||
|
||||
Py_Initialize();
|
||||
|
||||
// This is used by the import hook to locate the module libraries.
|
||||
Filename dtool_name = ExecutionEnvironment::get_dtool_name();
|
||||
string native_dir = dtool_name.get_dirname();
|
||||
PyObject *py_native_dir = PyUnicode_FromStringAndSize(native_dir.c_str(), native_dir.size());
|
||||
PySys_SetObject("_native_library_dir", py_native_dir);
|
||||
Py_DECREF(py_native_dir);
|
||||
|
||||
int sts = 1;
|
||||
FILE *fp = fopen(argv[1], "r");
|
||||
if (fp != nullptr) {
|
||||
int res = PyRun_AnyFile(fp, argv[1]);
|
||||
if (res > 0) {
|
||||
sts = 0;
|
||||
} else {
|
||||
android_cat.error() << "Error running " << argv[1] << "\n";
|
||||
PyErr_Print();
|
||||
}
|
||||
} else {
|
||||
android_cat.error() << "Unable to open " << argv[1] << "\n";
|
||||
}
|
||||
|
||||
Py_Finalize();
|
||||
return sts;
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# This script can be used for launching the Panda viewer from the Android
|
||||
# terminal environment, for example from within termux. It uses a socket
|
||||
# to pipe the command-line output back to the terminal.
|
||||
|
||||
port=12345
|
||||
|
||||
if [[ $# -eq 0 ]] ; then
|
||||
echo "Pass full path of model"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
am start --activity-clear-task -n org.panda3d.sdk/org.panda3d.android.PandaActivity --user 0 --es org.panda3d.OUTPUT_URI tcp://127.0.0.1:$port --grant-read-uri-permission --grant-write-uri-permission file://$(realpath $1)
|
||||
|
||||
nc -l -p $port
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# This script can be used for launching a Python script from the Android
|
||||
# terminal environment, for example from within termux. It uses a socket
|
||||
# to pipe the command-line output back to the terminal.
|
||||
|
||||
port=12345
|
||||
|
||||
if [[ $# -eq 0 ]] ; then
|
||||
echo "Pass full path of script"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
am start --activity-clear-task -n org.panda3d.sdk/org.panda3d.android.PythonActivity --user 0 --es org.panda3d.OUTPUT_URI tcp://127.0.0.1:$port --grant-read-uri-permission --grant-write-uri-permission file://$(realpath $1)
|
||||
|
||||
nc -l -p $port
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import sys
|
||||
import os
|
||||
|
||||
from importlib.abc import Loader, MetaPathFinder
|
||||
from importlib.machinery import ModuleSpec
|
||||
|
||||
if sys.version_info >= (3, 5):
|
||||
from importlib import _bootstrap_external
|
||||
else:
|
||||
from importlib import _bootstrap as _bootstrap_external
|
||||
|
||||
sys.platform = "android"
|
||||
|
||||
class AndroidExtensionFinder(MetaPathFinder):
|
||||
@classmethod
|
||||
def find_spec(cls, fullname, path=None, target=None):
|
||||
soname = 'libpy.' + fullname + '.so'
|
||||
path = os.path.join(sys._native_library_dir, soname)
|
||||
|
||||
if os.path.exists(path):
|
||||
loader = _bootstrap_external.ExtensionFileLoader(fullname, path)
|
||||
return ModuleSpec(fullname, loader, origin=path)
|
||||
|
||||
|
||||
def main():
|
||||
"""Adds the site-packages directory to the sys.path.
|
||||
Also, registers the import hook for extension modules."""
|
||||
|
||||
sys.path.append('{0}/lib/python{1}.{2}/site-packages'.format(sys.prefix, *sys.version_info))
|
||||
sys.meta_path.append(AndroidExtensionFinder)
|
||||
|
||||
|
||||
if not sys.flags.no_site:
|
||||
main()
|
||||
|
|
@ -309,8 +309,6 @@ open_window() {
|
|||
|
||||
_fb_properties = androidgsg->get_fb_properties();
|
||||
|
||||
androiddisplay_cat.error() << "open_window done\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -366,7 +364,6 @@ create_surface() {
|
|||
|
||||
// Create a context.
|
||||
if (androidgsg->_context == EGL_NO_CONTEXT) {
|
||||
androiddisplay_cat.error() << "creating context\n";
|
||||
if (!androidgsg->create_context()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,23 @@ ConfigVariableString openal_device
|
|||
PRC_DESC("Specify the OpenAL device string for audio playback (no quotes). If this "
|
||||
"is not specified, the OpenAL default device is used."));
|
||||
|
||||
ConfigVariableInt openal_buffer_delete_retries
|
||||
("openal-buffer-delete-retries", 5,
|
||||
PRC_DESC("If deleting a buffer fails due to still being in use, the OpenAL "
|
||||
"sound plugin will wait a moment and retry deletion, with an "
|
||||
"exponentially-increasing delay for each try. This number "
|
||||
"specifies how many repeat tries (not counting the initial try) "
|
||||
"should be made before giving up and raising an error."));
|
||||
|
||||
ConfigVariableDouble openal_buffer_delete_delay
|
||||
("openal-buffer-delete-delay", 0.001,
|
||||
PRC_DESC("If deleting a buffer fails due to still being in use, the OpenAL "
|
||||
"sound plugin will wait a moment and retry deletion, with an "
|
||||
"exponentially-increasing delay for each try. This number "
|
||||
"specifies how long, in seconds, the OpenAL plugin will wait after "
|
||||
"its first failed try. The second try will be double this "
|
||||
"delay, the third quadruple, and so on."));
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the library. This must be called at least once before any of
|
||||
|
|
|
|||
|
|
@ -26,5 +26,7 @@ extern "C" EXPCL_OPENAL_AUDIO void init_libOpenALAudio();
|
|||
extern "C" EXPCL_OPENAL_AUDIO Create_AudioManager_proc *get_audio_manager_func_openal_audio();
|
||||
|
||||
extern ConfigVariableString openal_device;
|
||||
extern ConfigVariableInt openal_buffer_delete_retries;
|
||||
extern ConfigVariableDouble openal_buffer_delete_delay;
|
||||
|
||||
#endif // CONFIG_OPENALAUDIO_H
|
||||
|
|
|
|||
|
|
@ -524,7 +524,7 @@ get_sound(const string &file_name, bool positional, int mode) {
|
|||
void OpenALAudioManager::
|
||||
uncache_sound(const string& file_name) {
|
||||
ReMutexHolder holder(_lock);
|
||||
assert(is_valid());
|
||||
nassertv(is_valid());
|
||||
Filename path = file_name;
|
||||
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
|
|
@ -910,7 +910,7 @@ reduce_sounds_playing_to(unsigned int count) {
|
|||
int limit = _sounds_playing.size() - count;
|
||||
while (limit-- > 0) {
|
||||
SoundsPlaying::iterator sound = _sounds_playing.begin();
|
||||
assert(sound != _sounds_playing.end());
|
||||
nassertv(sound != _sounds_playing.end());
|
||||
// When the user stops a sound, there is still a PT in the user's hand.
|
||||
// When we stop a sound here, however, this can remove the last PT. This
|
||||
// can cause an ugly recursion where stop calls the destructor, and the
|
||||
|
|
@ -1052,7 +1052,7 @@ OpenALAudioManager::SoundData::
|
|||
if (_sample != 0) {
|
||||
if (_manager->_is_valid) {
|
||||
_manager->make_current();
|
||||
alDeleteBuffers(1,&_sample);
|
||||
_manager->delete_buffer(_sample);
|
||||
}
|
||||
_sample = 0;
|
||||
}
|
||||
|
|
@ -1111,8 +1111,8 @@ discard_excess_cache(int sample_limit) {
|
|||
|
||||
while (((int)_expiring_samples.size()) > sample_limit) {
|
||||
SoundData *sd = (SoundData*)(_expiring_samples.front());
|
||||
assert(sd->_client_count == 0);
|
||||
assert(sd->_expire == _expiring_samples.begin());
|
||||
nassertv(sd->_client_count == 0);
|
||||
nassertv(sd->_expire == _expiring_samples.begin());
|
||||
_expiring_samples.pop_front();
|
||||
_sample_cache.erase(_sample_cache.find(sd->_movie->get_filename()));
|
||||
audio_debug("Expiring: " << sd->_movie->get_filename().get_basename());
|
||||
|
|
@ -1121,10 +1121,49 @@ discard_excess_cache(int sample_limit) {
|
|||
|
||||
while (((int)_expiring_streams.size()) > stream_limit) {
|
||||
SoundData *sd = (SoundData*)(_expiring_streams.front());
|
||||
assert(sd->_client_count == 0);
|
||||
assert(sd->_expire == _expiring_streams.begin());
|
||||
nassertv(sd->_client_count == 0);
|
||||
nassertv(sd->_expire == _expiring_streams.begin());
|
||||
_expiring_streams.pop_front();
|
||||
audio_debug("Expiring: " << sd->_movie->get_filename().get_basename());
|
||||
delete sd;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an OpenAL buffer. This is a special function because some
|
||||
* implementations of OpenAL (e.g. Apple's) don't unlock the buffers
|
||||
* immediately, due to needing to coordinate with another thread. If this is
|
||||
* the case, the alDeleteBuffers call will error back with AL_INVALID_OPERATION
|
||||
* as if trying to delete an actively-used buffer, which will tell us to wait a
|
||||
* bit and try again.
|
||||
*/
|
||||
void OpenALAudioManager::
|
||||
delete_buffer(ALuint buffer) {
|
||||
ReMutexHolder holder(_lock);
|
||||
int tries = 0;
|
||||
ALuint error;
|
||||
|
||||
// Keep trying until we succeed (or give up).
|
||||
while (true) {
|
||||
alDeleteBuffers(1, &buffer);
|
||||
error = alGetError();
|
||||
|
||||
if (error == AL_NO_ERROR) {
|
||||
// Success! This will happen right away 99% of the time.
|
||||
return;
|
||||
} else if (error != AL_INVALID_OPERATION) {
|
||||
// We weren't expecting that. This should be reported.
|
||||
break;
|
||||
} else if (tries >= openal_buffer_delete_retries.get_value()) {
|
||||
// We ran out of retries. Give up.
|
||||
break;
|
||||
} else {
|
||||
// Make another try after (delay * 2^n) seconds.
|
||||
Thread::sleep(openal_buffer_delete_delay.get_value() * (1 << tries));
|
||||
tries++;
|
||||
}
|
||||
}
|
||||
|
||||
// If we got here, one of the breaks above happened, indicating an error.
|
||||
audio_error("failed to delete a buffer: " << alGetString(error) );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,6 +129,8 @@ private:
|
|||
void decrement_client_count(SoundData *sd);
|
||||
void discard_excess_cache(int limit);
|
||||
|
||||
void delete_buffer(ALuint buffer);
|
||||
|
||||
void starting_sound(OpenALAudioSound* audio);
|
||||
void stopping_sound(OpenALAudioSound* audio);
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ get_calibrated_clock(double rtc) const {
|
|||
*
|
||||
* Returns true on success, false on failure.
|
||||
*/
|
||||
bool OpenALAudioSound::
|
||||
INLINE bool OpenALAudioSound::
|
||||
require_sound_data() {
|
||||
if (_sd==0) {
|
||||
_sd = _manager->get_sound_data(_movie, _desired_mode);
|
||||
|
|
@ -55,11 +55,48 @@ require_sound_data() {
|
|||
/**
|
||||
* Checks if the sound data record is present and releasable, and if so,
|
||||
* releases it.
|
||||
*
|
||||
* The sound data is "releasable" if it's from an ordinary, local file. Remote
|
||||
* streams cannot necessarily be reopened if lost, so we'll hold onto them if
|
||||
* so. The `force` argument overrides this, indicating we don't intend to
|
||||
* reacquire the sound data.
|
||||
*/
|
||||
void OpenALAudioSound::
|
||||
release_sound_data() {
|
||||
if ((_sd!=0) && (!_movie->get_filename().empty())) {
|
||||
INLINE void OpenALAudioSound::
|
||||
release_sound_data(bool force) {
|
||||
if (!has_sound_data()) return;
|
||||
|
||||
if (force || !_movie->get_filename().empty()) {
|
||||
_manager->decrement_client_count(_sd);
|
||||
_sd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the sound has NOT been cleaned up yet.
|
||||
*/
|
||||
INLINE bool OpenALAudioSound::
|
||||
is_valid() const {
|
||||
return _manager != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the sound is playing. This is per the OpenALAudioManager's
|
||||
* definition of "playing" -- as in, "will be called upon every update"
|
||||
*
|
||||
* This is mainly intended for use in asserts.
|
||||
*/
|
||||
INLINE bool OpenALAudioSound::
|
||||
is_playing() const {
|
||||
// Manager only gives us a _source if we need it (to talk to OpenAL), so:
|
||||
return _source != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the sound has its SoundData structure open at the moment.
|
||||
*
|
||||
* This is mainly intended for use in asserts.
|
||||
*/
|
||||
INLINE bool OpenALAudioSound::
|
||||
has_sound_data() const {
|
||||
return _sd != NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ OpenALAudioSound(OpenALAudioManager* manager,
|
|||
audio_warning("stereo sound " << movie->get_filename() << " will not be spatialized");
|
||||
}
|
||||
}
|
||||
release_sound_data();
|
||||
release_sound_data(false);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -99,15 +99,14 @@ OpenALAudioSound::
|
|||
void OpenALAudioSound::
|
||||
cleanup() {
|
||||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
if (_manager == 0) {
|
||||
if (!is_valid()) {
|
||||
return;
|
||||
}
|
||||
if (_source) {
|
||||
if (is_playing()) {
|
||||
stop();
|
||||
}
|
||||
if (_sd) {
|
||||
_manager->decrement_client_count(_sd);
|
||||
_sd = 0;
|
||||
if (has_sound_data()) {
|
||||
release_sound_data(true);
|
||||
}
|
||||
_manager->release_sound(this);
|
||||
_manager = 0;
|
||||
|
|
@ -119,7 +118,8 @@ cleanup() {
|
|||
void OpenALAudioSound::
|
||||
play() {
|
||||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
if (_manager == 0) return;
|
||||
|
||||
if (!is_valid()) return;
|
||||
|
||||
PN_stdfloat px,py,pz,vx,vy,vz;
|
||||
|
||||
|
|
@ -136,7 +136,7 @@ play() {
|
|||
}
|
||||
|
||||
_manager->starting_sound(this);
|
||||
if (!_source) {
|
||||
if (!is_playing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -195,11 +195,14 @@ play() {
|
|||
void OpenALAudioSound::
|
||||
stop() {
|
||||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
if (_manager==0) return;
|
||||
|
||||
if (_source) {
|
||||
if (!is_valid()) return;
|
||||
|
||||
if (is_playing()) {
|
||||
_manager->make_current();
|
||||
|
||||
nassertv(has_sound_data());
|
||||
|
||||
alGetError(); // clear errors
|
||||
alSourceStop(_source);
|
||||
al_audio_errcheck("stopping a source");
|
||||
|
|
@ -208,15 +211,14 @@ stop() {
|
|||
for (int i=0; i<((int)(_stream_queued.size())); i++) {
|
||||
ALuint buffer = _stream_queued[i]._buffer;
|
||||
if (buffer != _sd->_sample) {
|
||||
alDeleteBuffers(1, &buffer);
|
||||
al_audio_errcheck("deleting a buffer");
|
||||
_manager->delete_buffer(buffer);
|
||||
}
|
||||
}
|
||||
_stream_queued.resize(0);
|
||||
}
|
||||
|
||||
_manager->stopping_sound(this);
|
||||
release_sound_data();
|
||||
release_sound_data(false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -225,6 +227,9 @@ stop() {
|
|||
void OpenALAudioSound::
|
||||
finished() {
|
||||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
|
||||
if (!is_valid()) return;
|
||||
|
||||
stop();
|
||||
_current_time = _length;
|
||||
if (!_finished_event.empty()) {
|
||||
|
|
@ -255,7 +260,8 @@ get_loop() const {
|
|||
void OpenALAudioSound::
|
||||
set_loop_count(unsigned long loop_count) {
|
||||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
if (_manager==0) return;
|
||||
|
||||
if (!is_valid()) return;
|
||||
|
||||
if (loop_count >= 1000000000) {
|
||||
loop_count = 0;
|
||||
|
|
@ -282,9 +288,14 @@ void OpenALAudioSound::
|
|||
restart_stalled_audio() {
|
||||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
ALenum status;
|
||||
|
||||
if (!is_valid()) return;
|
||||
nassertv(is_playing());
|
||||
|
||||
if (_stream_queued.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
alGetError();
|
||||
alGetSourcei(_source, AL_SOURCE_STATE, &status);
|
||||
if (status != AL_PLAYING) {
|
||||
|
|
@ -298,6 +309,9 @@ restart_stalled_audio() {
|
|||
void OpenALAudioSound::
|
||||
queue_buffer(ALuint buffer, int samples, int loop_index, double time_offset) {
|
||||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
|
||||
nassertv(is_playing());
|
||||
|
||||
// Now push the buffer into the stream queue.
|
||||
alGetError();
|
||||
alSourceQueueBuffers(_source,1,&buffer);
|
||||
|
|
@ -322,6 +336,8 @@ ALuint OpenALAudioSound::
|
|||
make_buffer(int samples, int channels, int rate, unsigned char *data) {
|
||||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
|
||||
nassertr(is_playing(), 0);
|
||||
|
||||
// Allocate a buffer to hold the data.
|
||||
alGetError();
|
||||
ALuint buffer;
|
||||
|
|
@ -354,6 +370,8 @@ int OpenALAudioSound::
|
|||
read_stream_data(int bytelen, unsigned char *buffer) {
|
||||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
|
||||
nassertr(has_sound_data(), 0);
|
||||
|
||||
MovieAudioCursor *cursor = _sd->_stream;
|
||||
double length = cursor->length();
|
||||
int channels = cursor->audio_channels();
|
||||
|
|
@ -404,6 +422,9 @@ read_stream_data(int bytelen, unsigned char *buffer) {
|
|||
void OpenALAudioSound::
|
||||
correct_calibrated_clock(double rtc, double t) {
|
||||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
|
||||
nassertv(is_playing());
|
||||
|
||||
double cc = (rtc - _calibrated_clock_base) * _calibrated_clock_scale;
|
||||
double diff = cc-t;
|
||||
_calibrated_clock_decavg = (_calibrated_clock_decavg * 0.95) + (diff * 0.05);
|
||||
|
|
@ -435,6 +456,11 @@ correct_calibrated_clock(double rtc, double t) {
|
|||
void OpenALAudioSound::
|
||||
pull_used_buffers() {
|
||||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
|
||||
if (!is_valid()) return;
|
||||
nassertv(is_playing());
|
||||
nassertv(has_sound_data());
|
||||
|
||||
while (_stream_queued.size()) {
|
||||
ALuint buffer = 0;
|
||||
ALint num_buffers = 0;
|
||||
|
|
@ -446,18 +472,34 @@ pull_used_buffers() {
|
|||
int err = alGetError();
|
||||
if (err == AL_NO_ERROR) {
|
||||
if (_stream_queued[0]._buffer != buffer) {
|
||||
audio_error("corruption in stream queue");
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
_stream_queued.pop_front();
|
||||
if (_stream_queued.size()) {
|
||||
double al = _stream_queued[0]._time_offset + _stream_queued[0]._loop_index * _length;
|
||||
double rtc = TrueClock::get_global_ptr()->get_short_time();
|
||||
correct_calibrated_clock(rtc, al);
|
||||
}
|
||||
if (buffer != _sd->_sample) {
|
||||
alDeleteBuffers(1,&buffer);
|
||||
// This is certainly atypical: most implementations of OpenAL unqueue
|
||||
// buffers in FIFO order. However, some (e.g. Apple's) can unqueue
|
||||
// buffers out-of-order if playback is interrupted. So, we don't freak
|
||||
// out unless `buffer` isn't in _stream_queued at all.
|
||||
bool found_culprit = false;
|
||||
for (auto it = _stream_queued.begin(); it != _stream_queued.end(); ++it) {
|
||||
if (it->_buffer == buffer) {
|
||||
// Phew. Found it. Just remove that.
|
||||
_stream_queued.erase(it);
|
||||
found_culprit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_culprit) {
|
||||
audio_error("corruption in stream queue");
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
_stream_queued.pop_front();
|
||||
if (_stream_queued.size()) {
|
||||
double al = _stream_queued[0]._time_offset + _stream_queued[0]._loop_index * _length;
|
||||
double rtc = TrueClock::get_global_ptr()->get_short_time();
|
||||
correct_calibrated_clock(rtc, al);
|
||||
}
|
||||
if (buffer != _sd->_sample) {
|
||||
_manager->delete_buffer(buffer);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
|
|
@ -474,6 +516,10 @@ push_fresh_buffers() {
|
|||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
static unsigned char data[65536];
|
||||
|
||||
if (!is_valid()) return;
|
||||
nassertv(is_playing());
|
||||
nassertv(has_sound_data());
|
||||
|
||||
if (_sd->_sample) {
|
||||
while ((_loops_completed < _playing_loops) &&
|
||||
(_stream_queued.size() < 100)) {
|
||||
|
|
@ -499,9 +545,9 @@ push_fresh_buffers() {
|
|||
break;
|
||||
}
|
||||
ALuint buffer = make_buffer(samples, channels, rate, data);
|
||||
if (_manager == 0) return;
|
||||
if (!is_valid() || !buffer) return;
|
||||
queue_buffer(buffer, samples, loop_index, time_offset);
|
||||
if (_manager == 0) return;
|
||||
if (!is_valid()) return;
|
||||
fill += samples;
|
||||
}
|
||||
}
|
||||
|
|
@ -523,7 +569,7 @@ set_time(PN_stdfloat time) {
|
|||
PN_stdfloat OpenALAudioSound::
|
||||
get_time() const {
|
||||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
if (_manager == 0) {
|
||||
if (!is_valid()) {
|
||||
return 0.0;
|
||||
}
|
||||
return _current_time;
|
||||
|
|
@ -535,7 +581,9 @@ get_time() const {
|
|||
void OpenALAudioSound::
|
||||
cache_time(double rtc) {
|
||||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
assert(_source != 0);
|
||||
|
||||
nassertv(is_playing());
|
||||
|
||||
double t=get_calibrated_clock(rtc);
|
||||
double max = _length * _playing_loops;
|
||||
if (t >= max) {
|
||||
|
|
@ -553,7 +601,7 @@ set_volume(PN_stdfloat volume) {
|
|||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
_volume=volume;
|
||||
|
||||
if (_source) {
|
||||
if (is_playing()) {
|
||||
volume*=_manager->get_volume();
|
||||
_manager->make_current();
|
||||
alGetError(); // clear errors
|
||||
|
|
@ -597,7 +645,7 @@ void OpenALAudioSound::
|
|||
set_play_rate(PN_stdfloat play_rate) {
|
||||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
_play_rate = play_rate;
|
||||
if (_source) {
|
||||
if (is_playing()) {
|
||||
alSourcef(_source, AL_PITCH, play_rate);
|
||||
}
|
||||
}
|
||||
|
|
@ -639,7 +687,7 @@ set_3d_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx
|
|||
_velocity[1] = vz;
|
||||
_velocity[2] = -vy;
|
||||
|
||||
if (_source) {
|
||||
if (is_playing()) {
|
||||
_manager->make_current();
|
||||
|
||||
alGetError(); // clear errors
|
||||
|
|
@ -675,7 +723,7 @@ set_3d_min_distance(PN_stdfloat dist) {
|
|||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
_min_dist = dist;
|
||||
|
||||
if (_source) {
|
||||
if (is_playing()) {
|
||||
_manager->make_current();
|
||||
|
||||
alGetError(); // clear errors
|
||||
|
|
@ -700,7 +748,7 @@ set_3d_max_distance(PN_stdfloat dist) {
|
|||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
_max_dist = dist;
|
||||
|
||||
if (_source) {
|
||||
if (is_playing()) {
|
||||
_manager->make_current();
|
||||
|
||||
alGetError(); // clear errors
|
||||
|
|
@ -725,7 +773,7 @@ set_3d_drop_off_factor(PN_stdfloat factor) {
|
|||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
_drop_off_factor = factor;
|
||||
|
||||
if (_source) {
|
||||
if (is_playing()) {
|
||||
_manager->make_current();
|
||||
|
||||
alGetError(); // clear errors
|
||||
|
|
@ -743,13 +791,16 @@ get_3d_drop_off_factor() const {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets whether the sound is marked "active". By default, the active flag
|
||||
* Sets whether the sound is marked "active". By default, the active flag is
|
||||
* true for all sounds. If the active flag is set to false for any particular
|
||||
* sound, the sound will not be heard.
|
||||
*/
|
||||
void OpenALAudioSound::
|
||||
set_active(bool active) {
|
||||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
|
||||
if (!is_valid()) return;
|
||||
|
||||
if (_active!=active) {
|
||||
_active=active;
|
||||
if (_active) {
|
||||
|
|
@ -813,7 +864,7 @@ get_name() const {
|
|||
AudioSound::SoundStatus OpenALAudioSound::
|
||||
status() const {
|
||||
ReMutexHolder holder(OpenALAudioManager::_lock);
|
||||
if (_source==0) {
|
||||
if (!is_playing()) {
|
||||
return AudioSound::READY;
|
||||
}
|
||||
if ((_loops_completed >= _playing_loops)&&(_stream_queued.size()==0)) {
|
||||
|
|
|
|||
|
|
@ -117,12 +117,14 @@ private:
|
|||
void pull_used_buffers();
|
||||
void push_fresh_buffers();
|
||||
INLINE bool require_sound_data();
|
||||
INLINE void release_sound_data();
|
||||
INLINE void release_sound_data(bool force);
|
||||
|
||||
INLINE bool is_valid() const;
|
||||
INLINE bool is_playing() const;
|
||||
INLINE bool has_sound_data() const;
|
||||
|
||||
private:
|
||||
|
||||
void do_stop();
|
||||
|
||||
PT(MovieAudio) _movie;
|
||||
OpenALAudioManager::SoundData *_sd;
|
||||
|
||||
|
|
|
|||
|
|
@ -1104,6 +1104,24 @@ get_group_collision_flag(unsigned int group1, unsigned int group2) const {
|
|||
return _filter_cb2._collide[group1].get_bit(group2);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void BulletWorld::
|
||||
set_force_update_all_aabbs(bool force) {
|
||||
LightMutexHolder holder(get_global_lock());
|
||||
_world->setForceUpdateAllAabbs(force);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
bool BulletWorld::
|
||||
get_force_update_all_aabbs() const {
|
||||
LightMutexHolder holder(get_global_lock());
|
||||
return _world->getForceUpdateAllAabbs();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -134,6 +134,9 @@ PUBLISHED:
|
|||
void set_group_collision_flag(unsigned int group1, unsigned int group2, bool enable);
|
||||
bool get_group_collision_flag(unsigned int group1, unsigned int group2) const;
|
||||
|
||||
void set_force_update_all_aabbs(bool force);
|
||||
bool get_force_update_all_aabbs() const;
|
||||
|
||||
// Callbacks
|
||||
void set_contact_added_callback(CallbackObject *obj);
|
||||
void clear_contact_added_callback();
|
||||
|
|
@ -166,6 +169,8 @@ PUBLISHED:
|
|||
MAKE_SEQ_PROPERTY(vehicles, get_num_vehicles, get_vehicle);
|
||||
MAKE_SEQ_PROPERTY(constraints, get_num_constraints, get_constraint);
|
||||
MAKE_SEQ_PROPERTY(manifolds, get_num_manifolds, get_manifold);
|
||||
MAKE_PROPERTY(force_update_all_aabbs, get_force_update_all_aabbs,
|
||||
set_force_update_all_aabbs);
|
||||
|
||||
PUBLISHED: // Deprecated methods, will be removed soon
|
||||
void attach_ghost(BulletGhostNode *node);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
#include "sliderTable.h"
|
||||
|
||||
class CharacterJointBundle;
|
||||
class ComputedVertices;
|
||||
|
||||
/**
|
||||
* An animated character, with skeleton-morph animation and either soft-
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ CharacterJoint(const CharacterJoint ©) :
|
|||
MovingPartMatrix(copy),
|
||||
_character(NULL),
|
||||
_net_transform(copy._net_transform),
|
||||
_initial_net_transform_inverse(copy._initial_net_transform_inverse)
|
||||
_initial_net_transform_inverse(copy._initial_net_transform_inverse),
|
||||
_skinning_matrix(copy._skinning_matrix)
|
||||
{
|
||||
// We don't copy the sets of transform nodes.
|
||||
}
|
||||
|
|
@ -60,9 +61,12 @@ CharacterJoint(Character *character,
|
|||
// update_internals() to get our _net_transform set properly.
|
||||
update_internals(root, parent, true, false, current_thread);
|
||||
|
||||
// And then compute its inverse. This is needed for ComputedVertices,
|
||||
// during animation.
|
||||
// And then compute its inverse. This is needed to track changes in
|
||||
// _net_transform as the joint moves, so we can recompute _skinning_matrix,
|
||||
// which maps vertices from their initial positions to their animated
|
||||
// positions.
|
||||
_initial_net_transform_inverse = invert(_net_transform);
|
||||
_skinning_matrix = LMatrix4::ident_mat();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -141,11 +145,13 @@ update_internals(PartBundle *root, PartGroup *parent, bool self_changed,
|
|||
}
|
||||
}
|
||||
|
||||
// Also tell our related JointVertexTransforms that they now need to
|
||||
// recompute themselves.
|
||||
// Recompute the transform used by any vertices animated by this joint.
|
||||
_skinning_matrix = _initial_net_transform_inverse * _net_transform;
|
||||
|
||||
// Also tell our related JointVertexTransforms that we've changed their
|
||||
// underlying matrix.
|
||||
VertexTransforms::iterator vti;
|
||||
for (vti = _vertex_transforms.begin(); vti != _vertex_transforms.end(); ++vti) {
|
||||
(*vti)->_matrix_stale = true;
|
||||
(*vti)->mark_modified(current_thread);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,6 +108,12 @@ public:
|
|||
LMatrix4 _net_transform;
|
||||
LMatrix4 _initial_net_transform_inverse;
|
||||
|
||||
// This is the product of the above; the matrix that gets applied to a
|
||||
// vertex (whose coordinates are in the coordinate space of the character
|
||||
// in its neutral pose) to transform it from its neutral position to its
|
||||
// animated position.
|
||||
LMatrix4 _skinning_matrix;
|
||||
|
||||
public:
|
||||
virtual TypeHandle get_type() const {
|
||||
return get_class_type();
|
||||
|
|
|
|||
|
|
@ -18,13 +18,3 @@ INLINE const CharacterJoint *JointVertexTransform::
|
|||
get_joint() const {
|
||||
return _joint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recomputes _matrix if it needs it.
|
||||
*/
|
||||
INLINE void JointVertexTransform::
|
||||
check_matrix() const {
|
||||
if (_matrix_stale) {
|
||||
((JointVertexTransform *)this)->compute_matrix();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,8 +24,7 @@ TypeHandle JointVertexTransform::_type_handle;
|
|||
* Constructs an invalid object; used only by the bam loader.
|
||||
*/
|
||||
JointVertexTransform::
|
||||
JointVertexTransform() :
|
||||
_matrix_stale(true)
|
||||
JointVertexTransform()
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -35,8 +34,7 @@ JointVertexTransform() :
|
|||
*/
|
||||
JointVertexTransform::
|
||||
JointVertexTransform(CharacterJoint *joint) :
|
||||
_joint(joint),
|
||||
_matrix_stale(true)
|
||||
_joint(joint)
|
||||
{
|
||||
// Tell the joint that we need to be informed when it moves.
|
||||
_joint->_vertex_transforms.insert(this);
|
||||
|
|
@ -57,8 +55,7 @@ JointVertexTransform::
|
|||
*/
|
||||
void JointVertexTransform::
|
||||
get_matrix(LMatrix4 &matrix) const {
|
||||
check_matrix();
|
||||
matrix = _matrix;
|
||||
matrix = _joint->_skinning_matrix;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -69,8 +66,7 @@ get_matrix(LMatrix4 &matrix) const {
|
|||
*/
|
||||
void JointVertexTransform::
|
||||
mult_matrix(LMatrix4 &result, const LMatrix4 &previous) const {
|
||||
check_matrix();
|
||||
result.multiply(_matrix, previous);
|
||||
result.multiply(_joint->_skinning_matrix, previous);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -80,9 +76,7 @@ mult_matrix(LMatrix4 &result, const LMatrix4 &previous) const {
|
|||
*/
|
||||
void JointVertexTransform::
|
||||
accumulate_matrix(LMatrix4 &accum, PN_stdfloat weight) const {
|
||||
check_matrix();
|
||||
|
||||
accum.accumulate(_matrix, weight);
|
||||
accum.accumulate(_joint->_skinning_matrix, weight);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -93,19 +87,6 @@ output(ostream &out) const {
|
|||
out << _joint->get_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recomputes _matrix if it needs it. Uses locking.
|
||||
*/
|
||||
void JointVertexTransform::
|
||||
compute_matrix() {
|
||||
LightMutexHolder holder(_lock);
|
||||
if (_matrix_stale) {
|
||||
_matrix = _joint->_initial_net_transform_inverse * _joint->_net_transform;
|
||||
_matrix_stale = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tells the BamReader how to create objects of type JointVertexTransform.
|
||||
*/
|
||||
|
|
@ -165,6 +146,5 @@ fillin(DatagramIterator &scan, BamReader *manager) {
|
|||
VertexTransform::fillin(scan, manager);
|
||||
|
||||
manager->read_pointer(scan);
|
||||
_matrix_stale = true;
|
||||
mark_modified(Thread::get_current_thread());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,15 +47,8 @@ PUBLISHED:
|
|||
virtual void output(ostream &out) const;
|
||||
|
||||
private:
|
||||
INLINE void check_matrix() const;
|
||||
void compute_matrix();
|
||||
|
||||
PT(CharacterJoint) _joint;
|
||||
|
||||
LMatrix4 _matrix;
|
||||
bool _matrix_stale;
|
||||
LightMutex _lock;
|
||||
|
||||
public:
|
||||
static void register_with_read_factory();
|
||||
virtual void write_datagram(BamWriter *manager, Datagram &dg);
|
||||
|
|
|
|||
|
|
@ -244,7 +244,6 @@ add_waiting_task(AsyncTask *task) {
|
|||
*/
|
||||
void AsyncFuture::
|
||||
wake_task(AsyncTask *task) {
|
||||
cerr << "waking task\n";
|
||||
AsyncTaskManager *manager = task->_manager;
|
||||
if (manager == nullptr) {
|
||||
// If it's an unscheduled task, schedule it on the same manager as the
|
||||
|
|
|
|||
|
|
@ -900,12 +900,52 @@ write_datagram(BamWriter *manager, Datagram &dg) {
|
|||
int ClipPlaneAttrib::
|
||||
complete_pointers(TypedWritable **p_list, BamReader *manager) {
|
||||
int pi = RenderAttrib::complete_pointers(p_list, manager);
|
||||
AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
|
||||
|
||||
if (manager->get_file_minor_ver() >= 40) {
|
||||
for (size_t i = 0; i < _off_planes.size(); ++i) {
|
||||
pi += _off_planes[i].complete_pointers(p_list + pi, manager);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < _on_planes.size(); ++i) {
|
||||
pi += _on_planes[i].complete_pointers(p_list + pi, manager);
|
||||
}
|
||||
|
||||
} else {
|
||||
BamAuxData *aux = (BamAuxData *)manager->get_aux_data(this, "planes");
|
||||
nassertr(aux != NULL, pi);
|
||||
|
||||
int i;
|
||||
aux->_off_list.reserve(aux->_num_off_planes);
|
||||
for (i = 0; i < aux->_num_off_planes; ++i) {
|
||||
PandaNode *node;
|
||||
DCAST_INTO_R(node, p_list[pi++], pi);
|
||||
aux->_off_list.push_back(node);
|
||||
}
|
||||
|
||||
aux->_on_list.reserve(aux->_num_on_planes);
|
||||
for (i = 0; i < aux->_num_on_planes; ++i) {
|
||||
PandaNode *node;
|
||||
DCAST_INTO_R(node, p_list[pi++], pi);
|
||||
aux->_on_list.push_back(node);
|
||||
}
|
||||
}
|
||||
|
||||
return pi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the BamReader to perform any final actions needed for setting up
|
||||
* the object after all objects have been read and all pointers have been
|
||||
* completed.
|
||||
*/
|
||||
void ClipPlaneAttrib::
|
||||
finalize(BamReader *manager) {
|
||||
if (manager->get_file_minor_ver() >= 40) {
|
||||
AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
|
||||
|
||||
// Check if any of the nodes we loaded are mentioned in the
|
||||
// AttribNodeRegistry. If so, replace them.
|
||||
for (size_t i = 0; i < _off_planes.size(); ++i) {
|
||||
int n = areg->find_node(_off_planes[i]);
|
||||
if (n != -1) {
|
||||
// If it's in the registry, replace it.
|
||||
|
|
@ -914,8 +954,6 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
|
|||
}
|
||||
|
||||
for (size_t i = 0; i < _on_planes.size(); ++i) {
|
||||
pi += _on_planes[i].complete_pointers(p_list + pi, manager);
|
||||
|
||||
int n = areg->find_node(_on_planes[i]);
|
||||
if (n != -1) {
|
||||
// If it's in the registry, replace it.
|
||||
|
|
@ -924,53 +962,46 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
|
|||
}
|
||||
|
||||
} else {
|
||||
Planes::iterator ci = _off_planes.begin();
|
||||
while (ci != _off_planes.end()) {
|
||||
PandaNode *node;
|
||||
DCAST_INTO_R(node, p_list[pi++], pi);
|
||||
// Now it's safe to convert our saved PandaNodes into NodePaths.
|
||||
BamAuxData *aux = (BamAuxData *)manager->get_aux_data(this, "planes");
|
||||
nassertv(aux != NULL);
|
||||
nassertv(aux->_num_off_planes == (int)aux->_off_list.size());
|
||||
nassertv(aux->_num_on_planes == (int)aux->_on_list.size());
|
||||
|
||||
// We go through some effort to look up the node in the registry without
|
||||
// creating a NodePath around it first (which would up, and then down,
|
||||
// the reference count, possibly deleting the node).
|
||||
int ni = areg->find_node(node->get_type(), node->get_name());
|
||||
if (ni != -1) {
|
||||
(*ci) = areg->get_node(ni);
|
||||
AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
|
||||
|
||||
_off_planes.reserve(aux->_off_list.size());
|
||||
NodeList::iterator ni;
|
||||
for (ni = aux->_off_list.begin(); ni != aux->_off_list.end(); ++ni) {
|
||||
PandaNode *node = (*ni);
|
||||
int n = areg->find_node(node->get_type(), node->get_name());
|
||||
if (n != -1) {
|
||||
// If it's in the registry, add that NodePath.
|
||||
_off_planes.push_back(areg->get_node(n));
|
||||
} else {
|
||||
(*ci) = NodePath(node);
|
||||
// Otherwise, add any arbitrary NodePath. Complain if it's ambiguous.
|
||||
_off_planes.push_back(NodePath(node));
|
||||
}
|
||||
++ci;
|
||||
}
|
||||
|
||||
ci = _on_planes.begin();
|
||||
while (ci != _on_planes.end()) {
|
||||
PandaNode *node;
|
||||
DCAST_INTO_R(node, p_list[pi++], pi);
|
||||
|
||||
int ni = areg->find_node(node->get_type(), node->get_name());
|
||||
if (ni != -1) {
|
||||
(*ci) = areg->get_node(ni);
|
||||
_on_planes.reserve(aux->_on_list.size());
|
||||
for (ni = aux->_on_list.begin(); ni != aux->_on_list.end(); ++ni) {
|
||||
PandaNode *node = (*ni);
|
||||
int n = areg->find_node(node->get_type(), node->get_name());
|
||||
if (n != -1) {
|
||||
// If it's in the registry, add that NodePath.
|
||||
_on_planes.push_back(areg->get_node(n));
|
||||
node = _on_planes.back().node();
|
||||
} else {
|
||||
(*ci) = NodePath(node);
|
||||
// Otherwise, add any arbitrary NodePath. Complain if it's ambiguous.
|
||||
_on_planes.push_back(NodePath(node));
|
||||
}
|
||||
++ci;
|
||||
}
|
||||
}
|
||||
|
||||
// Now that the NodePaths have been filled in, we can sort the list.
|
||||
_off_planes.sort();
|
||||
_on_planes.sort();
|
||||
|
||||
return pi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some objects require all of their nested pointers to have been completed
|
||||
* before the objects themselves can be completed. If this is the case,
|
||||
* override this method to return true, and be careful with circular
|
||||
* references (which would make the object unreadable from a bam file).
|
||||
*/
|
||||
bool ClipPlaneAttrib::
|
||||
require_fully_complete() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -987,6 +1018,8 @@ make_from_bam(const FactoryParams ¶ms) {
|
|||
parse_params(params, scan, manager);
|
||||
attrib->fillin(scan, manager);
|
||||
|
||||
manager->register_finalize(attrib);
|
||||
|
||||
return attrib;
|
||||
}
|
||||
|
||||
|
|
@ -1000,26 +1033,24 @@ fillin(DatagramIterator &scan, BamReader *manager) {
|
|||
|
||||
_off_all_planes = scan.get_bool();
|
||||
|
||||
int num_off_planes = scan.get_uint16();
|
||||
|
||||
// Push back an empty NodePath for each off Plane for now, until we get the
|
||||
// actual list of pointers later in complete_pointers().
|
||||
_off_planes.resize(num_off_planes);
|
||||
if (manager->get_file_minor_ver() >= 40) {
|
||||
for (int i = 0; i < num_off_planes; i++) {
|
||||
_off_planes.resize(scan.get_uint16());
|
||||
for (size_t i = 0; i < _off_planes.size(); ++i) {
|
||||
_off_planes[i].fillin(scan, manager);
|
||||
}
|
||||
} else {
|
||||
manager->read_pointers(scan, num_off_planes);
|
||||
}
|
||||
|
||||
int num_on_planes = scan.get_uint16();
|
||||
_on_planes.resize(num_on_planes);
|
||||
if (manager->get_file_minor_ver() >= 40) {
|
||||
for (int i = 0; i < num_on_planes; i++) {
|
||||
manager->read_pointer(scan);
|
||||
_on_planes.resize(scan.get_uint16());
|
||||
for (size_t i = 0; i < _on_planes.size(); ++i) {
|
||||
_on_planes[i].fillin(scan, manager);
|
||||
}
|
||||
} else {
|
||||
manager->read_pointers(scan, num_on_planes);
|
||||
BamAuxData *aux = new BamAuxData;
|
||||
manager->set_aux_data(this, "planes", aux);
|
||||
|
||||
aux->_num_off_planes = scan.get_uint16();
|
||||
manager->read_pointers(scan, aux->_num_off_planes);
|
||||
|
||||
aux->_num_on_planes = scan.get_uint16();
|
||||
manager->read_pointers(scan, aux->_num_on_planes);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,11 +125,26 @@ PUBLISHED:
|
|||
}
|
||||
MAKE_PROPERTY(class_slot, get_class_slot);
|
||||
|
||||
public:
|
||||
// This data is only needed when reading from a bam file.
|
||||
typedef pvector<PT(PandaNode) > NodeList;
|
||||
class BamAuxData : public BamReader::AuxData {
|
||||
public:
|
||||
// We hold a pointer to each of the PandaNodes on the on_list and
|
||||
// off_list. We will later convert these to NodePaths in
|
||||
// finalize().
|
||||
int _num_off_planes;
|
||||
int _num_on_planes;
|
||||
NodeList _off_list;
|
||||
NodeList _on_list;
|
||||
};
|
||||
|
||||
public:
|
||||
static void register_with_read_factory();
|
||||
virtual void write_datagram(BamWriter *manager, Datagram &dg);
|
||||
virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
|
||||
virtual bool require_fully_complete() const;
|
||||
|
||||
virtual void finalize(BamReader *manager);
|
||||
|
||||
protected:
|
||||
static TypedWritable *make_from_bam(const FactoryParams ¶ms);
|
||||
|
|
|
|||
|
|
@ -128,6 +128,29 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
|
|||
object->_state = object->_state->compose(get_rescale_normal_state(mode));
|
||||
}
|
||||
|
||||
// Check for a special wireframe setting.
|
||||
const RenderModeAttrib *rmode;
|
||||
if (object->_state->get_attrib(rmode)) {
|
||||
if (rmode->get_mode() == RenderModeAttrib::M_filled_wireframe) {
|
||||
CullableObject *wireframe_part = new CullableObject(*object);
|
||||
wireframe_part->_state = get_wireframe_overlay_state(rmode);
|
||||
|
||||
if (wireframe_part->munge_geom
|
||||
(_gsg, _gsg->get_geom_munger(wireframe_part->_state, current_thread),
|
||||
traverser, force)) {
|
||||
int wireframe_bin_index = bin_manager->find_bin("fixed");
|
||||
CullBin *bin = get_bin(wireframe_bin_index);
|
||||
nassertv(bin != (CullBin *)NULL);
|
||||
check_flash_bin(wireframe_part->_state, bin_manager, wireframe_bin_index);
|
||||
bin->add_object(wireframe_part, current_thread);
|
||||
} else {
|
||||
delete wireframe_part;
|
||||
}
|
||||
|
||||
object->_state = object->_state->compose(get_wireframe_filled_state());
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if there's a special transparency setting.
|
||||
const TransparencyAttrib *trans;
|
||||
if (object->_state->get_attrib(trans)) {
|
||||
|
|
@ -216,29 +239,6 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
|
|||
}
|
||||
}
|
||||
|
||||
// Check for a special wireframe setting.
|
||||
const RenderModeAttrib *rmode;
|
||||
if (object->_state->get_attrib(rmode)) {
|
||||
if (rmode->get_mode() == RenderModeAttrib::M_filled_wireframe) {
|
||||
CullableObject *wireframe_part = new CullableObject(*object);
|
||||
wireframe_part->_state = get_wireframe_overlay_state(rmode);
|
||||
|
||||
if (wireframe_part->munge_geom
|
||||
(_gsg, _gsg->get_geom_munger(wireframe_part->_state, current_thread),
|
||||
traverser, force)) {
|
||||
int wireframe_bin_index = bin_manager->find_bin("fixed");
|
||||
CullBin *bin = get_bin(wireframe_bin_index);
|
||||
nassertv(bin != (CullBin *)NULL);
|
||||
check_flash_bin(wireframe_part->_state, bin_manager, wireframe_bin_index);
|
||||
bin->add_object(wireframe_part, current_thread);
|
||||
} else {
|
||||
delete wireframe_part;
|
||||
}
|
||||
|
||||
object->_state = object->_state->compose(get_wireframe_filled_state());
|
||||
}
|
||||
}
|
||||
|
||||
int bin_index = object->_state->get_bin_index();
|
||||
CullBin *bin = get_bin(bin_index);
|
||||
nassertv(bin != (CullBin *)NULL);
|
||||
|
|
|
|||
|
|
@ -344,7 +344,7 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) {
|
|||
|
||||
case TextureStage::M_modulate_gloss:
|
||||
if (shader_attrib->auto_gloss_on()) {
|
||||
info._flags = ShaderKey::TF_map_glow;
|
||||
info._flags = ShaderKey::TF_map_gloss;
|
||||
} else {
|
||||
info._mode = TextureStage::M_modulate;
|
||||
info._flags = ShaderKey::TF_has_rgb;
|
||||
|
|
|
|||
|
|
@ -1278,7 +1278,8 @@ p_read_object() {
|
|||
} else {
|
||||
if (bam_cat.is_spam()) {
|
||||
bam_cat.spam()
|
||||
<< "Read a " << object->get_type() << ": " << (void *)object << "\n";
|
||||
<< "Read a " << object->get_type() << ": " << (void *)object
|
||||
<< " (id=" << object_id << ")\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@
|
|||
Configure(config_vision);
|
||||
NotifyCategoryDef(vision, "");
|
||||
|
||||
ConfigVariableBool v4l_blocking
|
||||
("v4l-blocking", false,
|
||||
PRC_DESC("Set this to true if you want to block waiting for webcam frames."));
|
||||
|
||||
ConfigureFn(config_vision) {
|
||||
init_libvision();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,12 @@
|
|||
|
||||
#include "pandabase.h"
|
||||
#include "notifyCategoryProxy.h"
|
||||
#include "configVariableBool.h"
|
||||
|
||||
NotifyCategoryDecl(vision, EXPCL_VISION, EXPTP_VISION);
|
||||
|
||||
extern ConfigVariableBool v4l_blocking;
|
||||
|
||||
extern EXPCL_VISION void init_libvision();
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -209,7 +209,13 @@ WebcamVideoCursorV4L(WebcamVideoV4L *src) : MovieVideoCursor(src) {
|
|||
|
||||
_buffers = NULL;
|
||||
_buflens = NULL;
|
||||
_fd = open(src->_device.c_str(), O_RDWR);
|
||||
|
||||
int mode = O_RDWR;
|
||||
if (!v4l_blocking) {
|
||||
mode = O_NONBLOCK;
|
||||
}
|
||||
|
||||
_fd = open(src->_device.c_str(), mode);
|
||||
if (-1 == _fd) {
|
||||
vision_cat.error() << "Failed to open " << src->_device.c_str() << "\n";
|
||||
return;
|
||||
|
|
@ -247,6 +253,10 @@ WebcamVideoCursorV4L(WebcamVideoV4L *src) : MovieVideoCursor(src) {
|
|||
_num_components = 4;
|
||||
break;
|
||||
|
||||
case V4L2_PIX_FMT_GREY:
|
||||
_num_components = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
vision_cat.error() << "Unsupported pixel format " << src->get_pixel_format() << "!\n";
|
||||
_ready = false;
|
||||
|
|
@ -393,6 +403,10 @@ fetch_buffer() {
|
|||
vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
vbuf.memory = V4L2_MEMORY_MMAP;
|
||||
if (-1 == ioctl(_fd, VIDIOC_DQBUF, &vbuf) && errno != EIO) {
|
||||
if (errno == EAGAIN) {
|
||||
// Simply nothing is available yet.
|
||||
return NULL;
|
||||
}
|
||||
vision_cat.error() << "Failed to dequeue buffer!\n";
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -484,6 +498,7 @@ fetch_buffer() {
|
|||
|
||||
case V4L2_PIX_FMT_BGR24:
|
||||
case V4L2_PIX_FMT_BGR32:
|
||||
case V4L2_PIX_FMT_GREY:
|
||||
// Simplest case: copying every row verbatim.
|
||||
nassertr(old_bpl == new_bpl, NULL);
|
||||
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@ void find_all_webcams_v4l() {
|
|||
case V4L2_PIX_FMT_BGR32:
|
||||
case V4L2_PIX_FMT_RGB24:
|
||||
case V4L2_PIX_FMT_RGB32:
|
||||
case V4L2_PIX_FMT_GREY:
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -41,7 +41,9 @@ def window(graphics_pipe, graphics_engine):
|
|||
)
|
||||
graphics_engine.open_windows()
|
||||
|
||||
assert win is not None
|
||||
if win is None:
|
||||
pytest.skip("GraphicsPipe cannot make windows")
|
||||
|
||||
yield win
|
||||
|
||||
if win is not None:
|
||||
|
|
|
|||
Loading…
Reference in New Issue