diff --git a/README.md b/README.md index cf45f0dfd2..43feb9b19c 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,9 @@ Building Panda3D Windows ------- -We currently build using the Microsoft Visual C++ 2015 compiler. You will -also need to install the [Windows 10 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk), +You can build Panda3D with the Microsoft Visual C++ 2015 or 2017 compiler, +which can be downloaded for free from the [Visual Studio site](https://visualstudio.microsoft.com/downloads/). +You will also need to install the [Windows 10 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk), and if you intend to target Windows XP, you will also need the [Windows 7.1 SDK](https://www.microsoft.com/en-us/download/details.aspx?id=8279). @@ -58,11 +59,12 @@ http://rdb.name/thirdparty-vc14-x64.7z http://rdb.name/thirdparty-vc14.7z After acquiring these dependencies, you may simply build Panda3D from the -command prompt using the following command. (Add the `--windows-sdk=10` -option if you don't need to support Windows XP.) +command prompt using the following command. (Change `14.1` to `14` if you are +using Visual C++ 2015 instead of 2017. Add the `--windows-sdk=10` option if +you don't need to support Windows XP and did not install the Windows 7.1 SDK.) ```bash -makepanda\makepanda.bat --everything --installer --no-eigen --threads=2 +makepanda\makepanda.bat --everything --installer --msvc-version=14.1 --no-eigen --threads=2 ``` When the build succeeds, it will produce an .exe file that you can use to @@ -101,7 +103,7 @@ If you are on Ubuntu, this command should cover the most frequently used third-party packages: ```bash -sudo apt-get install build-essential pkg-config python-dev libpng-dev libjpeg-dev libtiff-dev zlib1g-dev libssl-dev libx11-dev libgl1-mesa-dev libxrandr-dev libxxf86dga-dev libxcursor-dev bison flex libfreetype6-dev libvorbis-dev libeigen3-dev libopenal-dev libode-dev libbullet-dev nvidia-cg-toolkit libgtk2.0-dev +sudo apt-get install build-essential pkg-config python-dev libpng-dev libjpeg-dev libtiff-dev zlib1g-dev libssl-dev libx11-dev libgl1-mesa-dev libxrandr-dev libxxf86dga-dev libxcursor-dev bison flex libfreetype6-dev libvorbis-dev libeigen3-dev libopenal-dev libode-dev libbullet-dev nvidia-cg-toolkit libgtk2.0-dev libassimp-dev libopenexr-dev ``` Once Panda3D has built, you can either install the .deb or .rpm package that @@ -163,6 +165,36 @@ python3.6 makepanda/makepanda.py --everything --installer --no-egl --no-gles --n If successful, this will produce a .pkg file in the root of the source directory which you can install using `pkg install`. +Android +------- + +Note: building on Android is very experimental and not guaranteed to work. + +You can experimentally build the Android Python runner via the [termux](https://termux.com/) +shell. You will need to install [Termux](https://play.google.com/store/apps/details?id=com.termux) +and [Termux API](https://play.google.com/store/apps/details?id=com.termux.api) +from the Play Store. Many of the dependencies can be installed by running the +following command in the Termux shell: + +```bash +pkg install python-dev termux-tools ndk-stl ndk-sysroot clang libvorbis-dev libopus-dev opusfile-dev openal-soft-dev freetype-dev harfbuzz-dev libpng-dev ecj4.6 dx patchelf aapt apksigner libcrypt-dev +``` + +Then, you can build and install the .apk right away using these commands: + +```bash +python makepanda/makepanda.py --everything --target android-21 --installer +xdg-open panda3d.apk +``` + +To launch a Python program from Termux, you can use the `run_python.sh` script +inside the `panda/src/android` directory. It will launch Python in a separate +activity, load it with the Python script you passed as argument, and use a +socket for returning the command-line output to the Termux shell. Do note +that this requires the Python application to reside on the SD card and that +Termux needs to be set up with access to the SD card (using the +`termux-setup-storage` command). + Running Tests ============= diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx index f579f1586d..15607c95ea 100644 --- a/dtool/src/interrogate/interrogateBuilder.cxx +++ b/dtool/src/interrogate/interrogateBuilder.cxx @@ -2943,7 +2943,7 @@ define_method(CPPInstance *function, InterrogateType &itype, // specifically flag get_class_type() as published. bool force_publish = false; if (function->get_simple_name() == "get_class_type" && - (function->_storage_class && CPPInstance::SC_static) != 0 && + (function->_storage_class & CPPInstance::SC_static) != 0 && function->_vis <= V_public) { force_publish = true; } diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx index e900ab0e7f..42bcdb9a35 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -235,8 +235,8 @@ PyObject *Dtool_Raise_AttributeError(PyObject *obj, const char *attribute) { "'%.100s' object has no attribute '%.200s'", Py_TYPE(obj)->tp_name, attribute); - Py_INCREF(PyExc_TypeError); - PyErr_Restore(PyExc_TypeError, message, nullptr); + Py_INCREF(PyExc_AttributeError); + PyErr_Restore(PyExc_AttributeError, message, nullptr); return nullptr; } diff --git a/makepanda/confauto.in b/makepanda/confauto.in index 608a6daf68..41655f1bbc 100644 --- a/makepanda/confauto.in +++ b/makepanda/confauto.in @@ -21,6 +21,11 @@ load-file-type egg pandaegg +# If we built with Assimp support, we can enable the Assimp loader, +# which allows us to load many model formats natively. + +load-file-type p3assimp + # These entries work very similar to load-file-type, except they are # used by the MovieVideo and MovieAudio code to determine which module # should be loaded in order to decode files of the given extension. diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index a7eb0900ba..6bf63bc0a3 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -2884,6 +2884,9 @@ else: # otherwise, disable it. confautoprc = confautoprc.replace('#st#', '#') +if PkgSkip("ASSIMP"): + confautoprc = confautoprc.replace("load-file-type p3assimp", "#load-file-type p3assimp") + if (os.path.isfile("makepanda/myconfig.in")): configprc = ReadFile("makepanda/myconfig.in") else: diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index dd637d7a9a..57a592f05d 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -593,8 +593,7 @@ remove_all_windows() { Windows old_windows; old_windows.swap(_windows); Windows::iterator wi; - for (wi = old_windows.begin(); wi != old_windows.end(); ++wi) { - GraphicsOutput *win = (*wi); + for (GraphicsOutput *win : old_windows) { nassertv(win != nullptr); do_remove_window(win, current_thread); GraphicsStateGuardian *gsg = win->get_gsg(); @@ -605,6 +604,14 @@ remove_all_windows() { { MutexHolder new_windows_holder(_new_windows_lock, current_thread); + for (GraphicsOutput *win : _new_windows) { + nassertv(win != nullptr); + do_remove_window(win, current_thread); + GraphicsStateGuardian *gsg = win->get_gsg(); + if (gsg != nullptr) { + gsg->release_all(); + } + } _new_windows.clear(); } diff --git a/panda/src/display/standardMunger.cxx b/panda/src/display/standardMunger.cxx index 5d2a3f5102..94f376e99b 100644 --- a/panda/src/display/standardMunger.cxx +++ b/panda/src/display/standardMunger.cxx @@ -36,7 +36,8 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state, _munge_color(false), _munge_color_scale(false), _auto_shader(false), - _shader_skinning(false) + _shader_skinning(false), + _remove_material(false) { const ShaderAttrib *shader_attrib; state->get_attrib_def(shader_attrib); @@ -94,6 +95,19 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state, // effort to detect this contrived situation and handle it correctly. } } + + // If we have no lights but do have a material, we will need to remove it so + // that it won't appear when we enable color scale via lighting. + const LightAttrib *light_attrib; + const MaterialAttrib *material_attrib; + if (get_gsg()->get_color_scale_via_lighting() && + (!state->get_attrib(light_attrib) || !light_attrib->has_any_on_light()) && + state->get_attrib(material_attrib) && + material_attrib->get_material() != nullptr && + shader_attrib->get_shader() == nullptr) { + _remove_material = true; + _should_munge_state = true; + } } /** @@ -291,6 +305,9 @@ compare_to_impl(const GeomMunger *other) const { if (_auto_shader != om->_auto_shader) { return (int)_auto_shader - (int)om->_auto_shader; } + if (_remove_material != om->_remove_material) { + return (int)_remove_material - (int)om->_remove_material; + } return StateMunger::compare_to_impl(other); } @@ -344,5 +361,9 @@ munge_state_impl(const RenderState *state) { munged_state = munged_state->remove_attrib(ColorScaleAttrib::get_class_slot()); } + if (_remove_material) { + munged_state = munged_state->remove_attrib(MaterialAttrib::get_class_slot()); + } + return munged_state; } diff --git a/panda/src/display/standardMunger.h b/panda/src/display/standardMunger.h index 05e8ee0344..5703fd01b5 100644 --- a/panda/src/display/standardMunger.h +++ b/panda/src/display/standardMunger.h @@ -55,6 +55,7 @@ private: bool _munge_color_scale; bool _auto_shader; bool _shader_skinning; + bool _remove_material; LColor _color; LVecBase4 _color_scale; diff --git a/panda/src/event/asyncTask.h b/panda/src/event/asyncTask.h index f5871ae2f2..5a7a2cd805 100644 --- a/panda/src/event/asyncTask.h +++ b/panda/src/event/asyncTask.h @@ -99,6 +99,27 @@ PUBLISHED: virtual void output(std::ostream &out) const; +PUBLISHED: + MAKE_PROPERTY(state, get_state); + MAKE_PROPERTY(alive, is_alive); + MAKE_PROPERTY(manager, get_manager); + + // The name of this task. + MAKE_PROPERTY(name, get_name, set_name); + + // This is a number guaranteed to be unique for each different AsyncTask + // object in the universe. + MAKE_PROPERTY(id, get_task_id); + + MAKE_PROPERTY(task_chain, get_task_chain, set_task_chain); + MAKE_PROPERTY(sort, get_sort, set_sort); + MAKE_PROPERTY(priority, get_priority, set_priority); + MAKE_PROPERTY(done_event, get_done_event, set_done_event); + + MAKE_PROPERTY(dt, get_dt); + MAKE_PROPERTY(max_dt, get_max_dt); + MAKE_PROPERTY(average_dt, get_average_dt); + protected: void jump_to_task_chain(AsyncTaskManager *manager); DoneStatus unlock_and_do_task(); diff --git a/panda/src/event/pythonTask.h b/panda/src/event/pythonTask.h index 3771d46d8b..06ff8b6fe4 100644 --- a/panda/src/event/pythonTask.h +++ b/panda/src/event/pythonTask.h @@ -61,9 +61,6 @@ PUBLISHED: int __clear__(); PUBLISHED: - // The name of this task. - MAKE_PROPERTY(name, get_name, set_name); - // The amount of seconds that have elapsed since the task was started, // according to the task manager's clock. MAKE_PROPERTY(time, get_elapsed_time); @@ -88,10 +85,6 @@ PUBLISHED: // according to the task manager's clock. MAKE_PROPERTY(frame, get_elapsed_frames); - // This is a number guaranteed to be unique for each different AsyncTask - // object in the universe. - MAKE_PROPERTY(id, get_task_id); - // This is a special variable to hold the instance dictionary in which // custom variables may be stored. PyObject *__dict__; diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index 66a9e6d407..eab850bdba 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -283,6 +283,13 @@ begin_frame(FrameMode mode, Thread *current_thread) { rebuild_bitplanes(); } + // The host window may not have had sRGB enabled, so we need to do this. +#ifndef OPENGLES + if (get_fb_properties().get_srgb_color()) { + glEnable(GL_FRAMEBUFFER_SRGB); + } +#endif + _gsg->set_current_properties(&get_fb_properties()); report_my_gl_errors(); return true; diff --git a/panda/src/mathutil/mersenne.h b/panda/src/mathutil/mersenne.h index 7bcb00cb8f..15d96029e4 100644 --- a/panda/src/mathutil/mersenne.h +++ b/panda/src/mathutil/mersenne.h @@ -69,14 +69,12 @@ PUBLISHED: }; private: - enum { - // Period parameters - N = 624, - M = 397, - MATRIX_A = 0x9908b0dfUL, // constant vector a - UPPER_MASK = 0x80000000UL, // most significant w-r bits - LOWER_MASK = 0x7fffffffUL, // least significant r bits - }; + // Period parameters + static const unsigned long N = 624; + static const unsigned long M = 397; + static const unsigned long MATRIX_A = 0x9908b0dfUL; // constant vector a + static const unsigned long UPPER_MASK = 0x80000000UL; // most significant w-r bits + static const unsigned long LOWER_MASK = 0x7fffffffUL; // least significant r bits unsigned long mt[N]; // the array for the state vector unsigned int mti; // mti==N+1 means mt[N] is not initialized diff --git a/panda/src/movies/movieTypeRegistry.cxx b/panda/src/movies/movieTypeRegistry.cxx index 51468ac2a4..5bc8228bb4 100644 --- a/panda/src/movies/movieTypeRegistry.cxx +++ b/panda/src/movies/movieTypeRegistry.cxx @@ -12,10 +12,12 @@ */ #include "movieTypeRegistry.h" + #include "string_utils.h" #include "config_movies.h" #include "config_putil.h" #include "load_dso.h" +#include "reMutexHolder.h" using std::endl; using std::string; diff --git a/tests/display/test_color_buffer.py b/tests/display/test_color_buffer.py new file mode 100644 index 0000000000..dab73cbbb8 --- /dev/null +++ b/tests/display/test_color_buffer.py @@ -0,0 +1,271 @@ +from panda3d import core +import pytest + +TEST_COLOR = core.LColor(1, 127/255.0, 0, 127/255.0) +TEST_COLOR_SCALE = core.LVecBase4(0.5, 0.5, 0.5, 0.5) +TEST_SCALED_COLOR = core.LColor(TEST_COLOR) +TEST_SCALED_COLOR.componentwise_mult(TEST_COLOR_SCALE) +FUZZ = 0.02 + + +@pytest.fixture(scope='session', params=[False, True], ids=["shader:off", "shader:auto"]) +def shader_attrib(request): + """Returns two ShaderAttribs: one with auto shader, one without.""" + if request.param: + return core.ShaderAttrib.make_default().set_shader_auto(True) + else: + return core.ShaderAttrib.make_off() + + +@pytest.fixture(scope='session', params=["mat:off", "mat:empty", "mat:amb", "mat:diff", "mat:both"]) +def material_attrib(request): + """Returns two MaterialAttribs: one with material, one without. It + shouldn't really matter what we set them to, since the tests in here do + not use lighting, and therefore the material should be ignored.""" + + if request.param == "mat:off": + return core.MaterialAttrib.make_off() + + elif request.param == "mat:empty": + return core.MaterialAttrib.make(core.Material()) + + elif request.param == "mat:amb": + mat = core.Material() + mat.ambient = (0.1, 1, 0.5, 1) + return core.MaterialAttrib.make(mat) + + elif request.param == "mat:diff": + mat = core.Material() + mat.diffuse = (0.1, 1, 0.5, 1) + return core.MaterialAttrib.make(mat) + + elif request.param == "mat:both": + mat = core.Material() + mat.diffuse = (0.1, 1, 0.5, 1) + mat.ambient = (0.1, 1, 0.5, 1) + return core.MaterialAttrib.make(mat) + + +@pytest.fixture(scope='module', params=[False, True], ids=["srgb:off", "srgb:on"]) +def color_region(request, graphics_pipe): + """Creates and returns a DisplayRegion with a depth buffer.""" + + engine = core.GraphicsEngine() + engine.set_threading_model("") + + host_fbprops = core.FrameBufferProperties() + host_fbprops.force_hardware = True + + host = engine.make_output( + graphics_pipe, + 'host', + 0, + host_fbprops, + core.WindowProperties.size(32, 32), + core.GraphicsPipe.BF_refuse_window, + ) + engine.open_windows() + + if host is None: + pytest.skip("GraphicsPipe cannot make offscreen buffers") + + fbprops = core.FrameBufferProperties() + fbprops.force_hardware = True + fbprops.set_rgba_bits(8, 8, 8, 8) + fbprops.srgb_color = request.param + + buffer = engine.make_output( + graphics_pipe, + 'buffer', + 0, + fbprops, + core.WindowProperties.size(32, 32), + core.GraphicsPipe.BF_refuse_window, + host.gsg, + host + ) + engine.open_windows() + + if buffer is None: + pytest.skip("Cannot make color buffer") + + if fbprops.srgb_color != buffer.get_fb_properties().srgb_color: + pytest.skip("Cannot make buffer with required srgb_color setting") + + buffer.set_clear_color_active(True) + buffer.set_clear_color((0, 0, 0, 1)) + + yield buffer.make_display_region() + + if buffer is not None: + engine.remove_window(buffer) + + +def render_color_pixel(region, state, vertex_color=None): + """Renders a fragment using the specified render settings, and returns the + resulting color value.""" + + # Set up the scene with a blank card rendering at specified distance. + scene = core.NodePath("root") + scene.set_attrib(core.DepthTestAttrib.make(core.RenderAttrib.M_always)) + + camera = scene.attach_new_node(core.Camera("camera")) + camera.node().get_lens(0).set_near_far(1, 3) + camera.node().set_cull_bounds(core.OmniBoundingVolume()) + + cm = core.CardMaker("card") + cm.set_frame(-1, 1, -1, 1) + + if vertex_color is not None: + cm.set_color(vertex_color) + + card = scene.attach_new_node(cm.generate()) + card.set_state(state) + card.set_pos(0, 2, 0) + card.set_scale(60) + + region.active = True + region.camera = camera + + color_texture = core.Texture("color") + region.window.add_render_texture(color_texture, + core.GraphicsOutput.RTM_copy_ram, + core.GraphicsOutput.RTP_color) + + region.window.engine.render_frame() + region.window.clear_render_textures() + + col = core.LColor() + color_texture.peek().lookup(col, 0.5, 0.5) + return col + + +def test_color_write_mask(color_region): + state = core.RenderState.make( + core.ColorWriteAttrib.make(core.ColorWriteAttrib.C_green), + ) + result = render_color_pixel(color_region, state) + assert result == (0, 1, 0, 1) + + +def test_color_empty(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state) + assert result == (1, 1, 1, 1) + + +def test_color_off(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + core.ColorAttrib.make_off(), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state) + assert result == (1, 1, 1, 1) + + +def test_color_flat(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + core.ColorAttrib.make_flat(TEST_COLOR), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state) + assert result.almost_equal(TEST_COLOR, FUZZ) + + +def test_color_vertex(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + core.ColorAttrib.make_vertex(), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state, vertex_color=TEST_COLOR) + assert result.almost_equal(TEST_COLOR, FUZZ) + + +def test_color_empty_vertex(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state, vertex_color=TEST_COLOR) + assert result.almost_equal(TEST_COLOR, FUZZ) + + +def test_color_off_vertex(color_region, shader_attrib, material_attrib): + #XXX This behaviour is really odd. + state = core.RenderState.make( + core.ColorAttrib.make_off(), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state, vertex_color=TEST_COLOR) + assert result.almost_equal(TEST_COLOR, FUZZ) + + +def test_scaled_color_empty(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state) + assert result == (1, 1, 1, 1) + + +def test_scaled_color_off(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + core.ColorAttrib.make_off(), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state) + assert result == (1, 1, 1, 1) + + +def test_scaled_color_flat(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + core.ColorAttrib.make_flat(TEST_COLOR), + core.ColorScaleAttrib.make(TEST_COLOR_SCALE), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state) + assert result.almost_equal(TEST_SCALED_COLOR, FUZZ) + + +def test_scaled_color_vertex(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + core.ColorAttrib.make_vertex(), + core.ColorScaleAttrib.make(TEST_COLOR_SCALE), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state, vertex_color=TEST_COLOR) + assert result.almost_equal(TEST_SCALED_COLOR, FUZZ) + + +def test_scaled_color_empty_vertex(color_region, shader_attrib, material_attrib): + state = core.RenderState.make( + core.ColorScaleAttrib.make(TEST_COLOR_SCALE), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state, vertex_color=TEST_COLOR) + assert result.almost_equal(TEST_SCALED_COLOR, FUZZ) + + +def test_scaled_color_off_vertex(color_region, shader_attrib, material_attrib): + #XXX This behaviour is really odd. + state = core.RenderState.make( + core.ColorAttrib.make_off(), + core.ColorScaleAttrib.make(TEST_COLOR_SCALE), + shader_attrib, + material_attrib, + ) + result = render_color_pixel(color_region, state, vertex_color=TEST_COLOR) + assert result.almost_equal(TEST_SCALED_COLOR, FUZZ) + diff --git a/tests/display/test_depth_buffer.py b/tests/display/test_depth_buffer.py index 8581fe47ea..c655758703 100644 --- a/tests/display/test_depth_buffer.py +++ b/tests/display/test_depth_buffer.py @@ -91,8 +91,6 @@ def render_depth_pixel(region, distance, near, far, clear=None, write=True): region.window.engine.render_frame() region.window.clear_render_textures() - depth_texture.write("test2.png") - col = core.LColor() depth_texture.peek().lookup(col, 0.5, 0.5) return col[0]