mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-28 15:53:55 -04:00
Merge branch 'release/1.10.x'
This commit is contained in:
commit
92185bcb28
@ -49,9 +49,9 @@ class MotionTrailFrame:
|
||||
class MotionTrail(NodePath, DirectObject):
|
||||
"""Generates smooth geometry-based motion trails behind a moving object.
|
||||
|
||||
To use this class, first define the shape of the cross-section of the trail
|
||||
by repeatedly calling `add_vertex()` and `set_vertex_color()`.
|
||||
When this is done, `update_vertices()` must be called.
|
||||
To use this class, first define the shape of the cross-section polygon that
|
||||
is to be extruded along the motion trail by calling `add_vertex()` and
|
||||
`set_vertex_color()`. When this is done, call `update_vertices()`.
|
||||
|
||||
To generate the motion trail, either call `register_motion_trail()`
|
||||
to have Panda update it automatically, or periodically call the method
|
||||
@ -82,6 +82,10 @@ class MotionTrail(NodePath, DirectObject):
|
||||
|
||||
@classmethod
|
||||
def setGlobalEnable(cls, enable):
|
||||
"""Set this to False to have the task stop updating all motion trails.
|
||||
This does not prevent updating them manually using the
|
||||
`update_motion_trail()` method.
|
||||
"""
|
||||
cls.global_enable = enable
|
||||
|
||||
def __init__(self, name, parent_node_path):
|
||||
@ -119,14 +123,14 @@ class MotionTrail(NodePath, DirectObject):
|
||||
self.continuous_motion_trail = True
|
||||
self.color_scale = 1.0
|
||||
|
||||
## How long the time window is for which the trail is computed. Can be
|
||||
## increased to obtain a longer trail, decreased for a shorter trail.
|
||||
#: How long the time window is for which the trail is computed. Can be
|
||||
#: increased to obtain a longer trail, decreased for a shorter trail.
|
||||
self.time_window = 1.0
|
||||
|
||||
## How often the trail updates, in seconds. The default is 0.0, which
|
||||
## has the trail updated every frame for the smoothest result. Higher
|
||||
## values will generate a choppier trail. The `use_nurbs` option can
|
||||
## compensate partially for this choppiness, however.
|
||||
#: How often the trail updates, in seconds. The default is 0.0, which
|
||||
#: has the trail updated every frame for the smoothest result. Higher
|
||||
#: values will generate a choppier trail. The `use_nurbs` option can
|
||||
#: compensate partially for this choppiness, however.
|
||||
self.sampling_time = 0.0
|
||||
|
||||
self.square_t = True
|
||||
@ -137,9 +141,9 @@ class MotionTrail(NodePath, DirectObject):
|
||||
# node path states
|
||||
self.reparentTo(parent_node_path)
|
||||
|
||||
## A `.GeomNode` object containing the generated geometry. By default
|
||||
## parented to the MotionTrail itself, but can be reparented elsewhere
|
||||
## if necessary.
|
||||
#: A `.GeomNode` object containing the generated geometry. By default
|
||||
#: parented to the MotionTrail itself, but can be reparented elsewhere
|
||||
#: if necessary.
|
||||
self.geom_node = GeomNode("motion_trail")
|
||||
self.geom_node.setBoundsType(BoundingVolume.BT_box)
|
||||
self.geom_node_path = self.attachNewNode(self.geom_node)
|
||||
@ -170,9 +174,11 @@ class MotionTrail(NodePath, DirectObject):
|
||||
|
||||
self.relative_to_render = False
|
||||
|
||||
## Set this to True to use a NURBS curve to generate a smooth trail,
|
||||
## even if the underlying animation or movement is janky.
|
||||
#: Set this to True to use a NURBS curve to generate a smooth trail,
|
||||
#: even if the underlying animation or movement is janky.
|
||||
self.use_nurbs = False
|
||||
|
||||
#: This can be changed to fine-tune the resolution of the NURBS curve.
|
||||
self.resolution_distance = 0.5
|
||||
|
||||
self.cmotion_trail = CMotionTrail()
|
||||
@ -249,7 +255,7 @@ class MotionTrail(NodePath, DirectObject):
|
||||
|
||||
def add_vertex(self, vertex_id, vertex_function=None, context=None, *,
|
||||
start_color=(1.0, 1.0, 1.0, 1.0), end_color=(0.0, 0.0, 0.0, 1.0)):
|
||||
"""This must be called repeatedly to define the polygon that forms the
|
||||
"""This must be called initially to define the polygon that forms the
|
||||
cross-section of the generated motion trail geometry. The first
|
||||
argument is a user-defined vertex identifier, the second is a function
|
||||
that will be called with three parameters that should return the
|
||||
@ -390,21 +396,21 @@ class MotionTrail(NodePath, DirectObject):
|
||||
|
||||
def add_geometry_quad(self, v0, v1, v2, v3, c0, c1, c2, c3, t0, t1, t2, t3):
|
||||
|
||||
self.vertex_writer.addData3f(v0 [0], v0 [1], v0 [2])
|
||||
self.vertex_writer.addData3f(v1 [0], v1 [1], v1 [2])
|
||||
self.vertex_writer.addData3f(v2 [0], v2 [1], v2 [2])
|
||||
self.vertex_writer.addData3f(v3 [0], v3 [1], v3 [2])
|
||||
self.vertex_writer.addData3(v0[0], v0[1], v0[2])
|
||||
self.vertex_writer.addData3(v1[0], v1[1], v1[2])
|
||||
self.vertex_writer.addData3(v2[0], v2[1], v2[2])
|
||||
self.vertex_writer.addData3(v3[0], v3[1], v3[2])
|
||||
|
||||
self.color_writer.addData4f(c0)
|
||||
self.color_writer.addData4f(c1)
|
||||
self.color_writer.addData4f(c2)
|
||||
self.color_writer.addData4f(c3)
|
||||
self.color_writer.addData4(c0)
|
||||
self.color_writer.addData4(c1)
|
||||
self.color_writer.addData4(c2)
|
||||
self.color_writer.addData4(c3)
|
||||
|
||||
if self.texture is not None:
|
||||
self.texture_writer.addData2f(t0)
|
||||
self.texture_writer.addData2f(t1)
|
||||
self.texture_writer.addData2f(t2)
|
||||
self.texture_writer.addData2f(t3)
|
||||
self.texture_writer.addData2(t0)
|
||||
self.texture_writer.addData2(t1)
|
||||
self.texture_writer.addData2(t2)
|
||||
self.texture_writer.addData2(t3)
|
||||
|
||||
vertex_index = self.vertex_index
|
||||
|
||||
|
@ -1151,7 +1151,7 @@ class ShowBase(DirectObject.DirectObject):
|
||||
Creates the render scene graph, the primary scene graph for
|
||||
rendering 3-d geometry.
|
||||
"""
|
||||
## This is the root of the 3-D scene graph.
|
||||
#: This is the root of the 3-D scene graph.
|
||||
self.render = NodePath('render')
|
||||
self.render.setAttrib(RescaleNormalAttrib.makeDefault())
|
||||
|
||||
@ -1170,7 +1170,7 @@ class ShowBase(DirectObject.DirectObject):
|
||||
# for the benefit of creating DirectGui elements before ShowBase.
|
||||
from . import ShowBaseGlobal
|
||||
|
||||
## This is the root of the 2-D scene graph.
|
||||
#: This is the root of the 2-D scene graph.
|
||||
self.render2d = ShowBaseGlobal.render2d
|
||||
|
||||
# Set up some overrides to turn off certain properties which
|
||||
@ -1191,12 +1191,12 @@ class ShowBase(DirectObject.DirectObject):
|
||||
self.render2d.setMaterialOff(1)
|
||||
self.render2d.setTwoSided(1)
|
||||
|
||||
## The normal 2-d DisplayRegion has an aspect ratio that
|
||||
## matches the window, but its coordinate system is square.
|
||||
## This means anything we parent to render2d gets stretched.
|
||||
## For things where that makes a difference, we set up
|
||||
## aspect2d, which scales things back to the right aspect
|
||||
## ratio along the X axis (Z is still from -1 to 1)
|
||||
#: The normal 2-d DisplayRegion has an aspect ratio that
|
||||
#: matches the window, but its coordinate system is square.
|
||||
#: This means anything we parent to render2d gets stretched.
|
||||
#: For things where that makes a difference, we set up
|
||||
#: aspect2d, which scales things back to the right aspect
|
||||
#: ratio along the X axis (Z is still from -1 to 1)
|
||||
self.aspect2d = ShowBaseGlobal.aspect2d
|
||||
|
||||
aspectRatio = self.getAspectRatio()
|
||||
@ -1204,13 +1204,13 @@ class ShowBase(DirectObject.DirectObject):
|
||||
|
||||
self.a2dBackground = self.aspect2d.attachNewNode("a2dBackground")
|
||||
|
||||
## The Z position of the top border of the aspect2d screen.
|
||||
#: The Z position of the top border of the aspect2d screen.
|
||||
self.a2dTop = 1.0
|
||||
## The Z position of the bottom border of the aspect2d screen.
|
||||
#: The Z position of the bottom border of the aspect2d screen.
|
||||
self.a2dBottom = -1.0
|
||||
## The X position of the left border of the aspect2d screen.
|
||||
#: The X position of the left border of the aspect2d screen.
|
||||
self.a2dLeft = -aspectRatio
|
||||
## The X position of the right border of the aspect2d screen.
|
||||
#: The X position of the right border of the aspect2d screen.
|
||||
self.a2dRight = aspectRatio
|
||||
|
||||
self.a2dTopCenter = self.aspect2d.attachNewNode("a2dTopCenter")
|
||||
@ -1250,9 +1250,9 @@ class ShowBase(DirectObject.DirectObject):
|
||||
self.a2dBottomRight.setPos(self.a2dRight, 0, self.a2dBottom)
|
||||
self.a2dBottomRightNs.setPos(self.a2dRight, 0, self.a2dBottom)
|
||||
|
||||
## This special root, pixel2d, uses units in pixels that are relative
|
||||
## to the window. The upperleft corner of the window is (0, 0),
|
||||
## the lowerleft corner is (xsize, -ysize), in this coordinate system.
|
||||
#: This special root, pixel2d, uses units in pixels that are relative
|
||||
#: to the window. The upperleft corner of the window is (0, 0),
|
||||
#: the lowerleft corner is (xsize, -ysize), in this coordinate system.
|
||||
self.pixel2d = self.render2d.attachNewNode(PGTop("pixel2d"))
|
||||
self.pixel2d.setPos(-1, 0, 1)
|
||||
xsize, ysize = self.getSize()
|
||||
@ -1282,25 +1282,25 @@ class ShowBase(DirectObject.DirectObject):
|
||||
self.render2dp.setMaterialOff(1)
|
||||
self.render2dp.setTwoSided(1)
|
||||
|
||||
## The normal 2-d DisplayRegion has an aspect ratio that
|
||||
## matches the window, but its coordinate system is square.
|
||||
## This means anything we parent to render2dp gets stretched.
|
||||
## For things where that makes a difference, we set up
|
||||
## aspect2dp, which scales things back to the right aspect
|
||||
## ratio along the X axis (Z is still from -1 to 1)
|
||||
#: The normal 2-d DisplayRegion has an aspect ratio that
|
||||
#: matches the window, but its coordinate system is square.
|
||||
#: This means anything we parent to render2dp gets stretched.
|
||||
#: For things where that makes a difference, we set up
|
||||
#: aspect2dp, which scales things back to the right aspect
|
||||
#: ratio along the X axis (Z is still from -1 to 1)
|
||||
self.aspect2dp = self.render2dp.attachNewNode(PGTop("aspect2dp"))
|
||||
self.aspect2dp.node().setStartSort(16384)
|
||||
|
||||
aspectRatio = self.getAspectRatio()
|
||||
self.aspect2dp.setScale(1.0 / aspectRatio, 1.0, 1.0)
|
||||
|
||||
## The Z position of the top border of the aspect2dp screen.
|
||||
#: The Z position of the top border of the aspect2dp screen.
|
||||
self.a2dpTop = 1.0
|
||||
## The Z position of the bottom border of the aspect2dp screen.
|
||||
#: The Z position of the bottom border of the aspect2dp screen.
|
||||
self.a2dpBottom = -1.0
|
||||
## The X position of the left border of the aspect2dp screen.
|
||||
#: The X position of the left border of the aspect2dp screen.
|
||||
self.a2dpLeft = -aspectRatio
|
||||
## The X position of the right border of the aspect2dp screen.
|
||||
#: The X position of the right border of the aspect2dp screen.
|
||||
self.a2dpRight = aspectRatio
|
||||
|
||||
self.a2dpTopCenter = self.aspect2dp.attachNewNode("a2dpTopCenter")
|
||||
@ -1324,9 +1324,9 @@ class ShowBase(DirectObject.DirectObject):
|
||||
self.a2dpBottomLeft.setPos(self.a2dpLeft, 0, self.a2dpBottom)
|
||||
self.a2dpBottomRight.setPos(self.a2dpRight, 0, self.a2dpBottom)
|
||||
|
||||
## This special root, pixel2d, uses units in pixels that are relative
|
||||
## to the window. The upperleft corner of the window is (0, 0),
|
||||
## the lowerleft corner is (xsize, -ysize), in this coordinate system.
|
||||
#: This special root, pixel2dp, uses units in pixels that are relative
|
||||
#: to the window. The upperleft corner of the window is (0, 0),
|
||||
#: the lowerleft corner is (xsize, -ysize), in this coordinate system.
|
||||
self.pixel2dp = self.render2dp.attachNewNode(PGTop("pixel2dp"))
|
||||
self.pixel2dp.node().setStartSort(16384)
|
||||
self.pixel2dp.setPos(-1, 0, 1)
|
||||
@ -1647,11 +1647,11 @@ class ShowBase(DirectObject.DirectObject):
|
||||
|
||||
mw = self.buttonThrowers[0].getParent()
|
||||
|
||||
## A special ButtonThrower to generate keyboard events and
|
||||
## include the time from the OS. This is separate only to
|
||||
## support legacy code that did not expect a time parameter; it
|
||||
## will eventually be folded into the normal ButtonThrower,
|
||||
## above.
|
||||
#: A special ButtonThrower to generate keyboard events and
|
||||
#: include the time from the OS. This is separate only to
|
||||
#: support legacy code that did not expect a time parameter; it
|
||||
#: will eventually be folded into the normal ButtonThrower,
|
||||
#: above.
|
||||
self.timeButtonThrower = mw.attachNewNode(ButtonThrower('timeButtons'))
|
||||
self.timeButtonThrower.node().setPrefix('time-')
|
||||
self.timeButtonThrower.node().setTimeFlag(1)
|
||||
|
@ -71,6 +71,8 @@ Miscellaneous
|
||||
* Fix texture transforms sometimes not being flattened (#1392)
|
||||
* Fix support for `#pragma include <file.glsl>` in GLSL shaders
|
||||
* Fix `ShaderBuffer.prepare()` not doing anything
|
||||
* Implement deepcopy for PointerToArray
|
||||
* Fix Texture deepcopy keeping a reference to the original RAM image
|
||||
* Fix bf-cbc encryption no longer working when building with OpenSSL 3.0
|
||||
* PandaNode bounds_type property was erroneously marked read-only
|
||||
* Fix warnings when copying OdeTriMeshGeom objects
|
||||
@ -81,8 +83,10 @@ Miscellaneous
|
||||
* Add various useful functions to interrogatedb module
|
||||
* Fix Python 3 issues unpacking uint types in Python 3 (#1380)
|
||||
* Fix interrogate syntax error with C++11-style attributes in declarators
|
||||
* Fix double-precision color values not being clamped by GeomVertexWriter
|
||||
* Fix regression with BufferViewer in double-precision build (#1365)
|
||||
* Fix `PandaNode.nested_vertices` not updating properly
|
||||
* Prevent Panda calculating bounding volume of Geom with custom bounding volume
|
||||
* Add `do_events()` and `process_event()` snake_case aliases in eventMgr
|
||||
* Support second arg of None in `replace_texture()` / `replace_material()`
|
||||
* Support `os.fspath()` for ConfigVariableFilename objects (#1406)
|
||||
|
@ -39,7 +39,8 @@
|
||||
// Ask all the windows whether they are OK to be closed.
|
||||
bool should_close = true;
|
||||
for (NSWindow *window in [app windows]) {
|
||||
if (![[window delegate] windowShouldClose:window]) {
|
||||
id<NSWindowDelegate> delegate = [window delegate];
|
||||
if (delegate != nil && ![delegate windowShouldClose:window]) {
|
||||
should_close = false;
|
||||
}
|
||||
}
|
||||
|
@ -121,6 +121,8 @@ PUBLISHED:
|
||||
|
||||
EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags));
|
||||
EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const);
|
||||
|
||||
EXTENSION(PointerToArray<Element> __deepcopy__(PyObject *memo) const);
|
||||
#endif
|
||||
|
||||
#else // CPPPARSER
|
||||
@ -279,6 +281,8 @@ PUBLISHED:
|
||||
|
||||
EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const);
|
||||
EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const);
|
||||
|
||||
EXTENSION(ConstPointerToArray<Element> __deepcopy__(PyObject *memo) const);
|
||||
#endif
|
||||
|
||||
#else // CPPPARSER
|
||||
|
@ -440,6 +440,20 @@ __releasebuffer__(PyObject *self, Py_buffer *view) const {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A special Python method that is invoked by copy.deepcopy(pta). This makes
|
||||
* sure that there is truly a unique copy of the array.
|
||||
*/
|
||||
template<class Element>
|
||||
INLINE PointerToArray<Element> Extension<PointerToArray<Element> >::
|
||||
__deepcopy__(PyObject *memo) const {
|
||||
PointerToArray<Element> copy;
|
||||
if (!this->_this->is_null()) {
|
||||
copy.v() = this->_this->v();
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used to implement the buffer protocol, in order to allow efficient
|
||||
* access to the array data through a Python multiview object.
|
||||
@ -610,3 +624,17 @@ __releasebuffer__(PyObject *self, Py_buffer *view) const {
|
||||
view->internal = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A special Python method that is invoked by copy.deepcopy(pta). This makes
|
||||
* sure that there is truly a unique copy of the array.
|
||||
*/
|
||||
template<class Element>
|
||||
INLINE ConstPointerToArray<Element> Extension<ConstPointerToArray<Element> >::
|
||||
__deepcopy__(PyObject *memo) const {
|
||||
PointerToArray<Element> copy;
|
||||
if (!this->_this->is_null()) {
|
||||
copy.v() = this->_this->v();
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ public:
|
||||
|
||||
INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags);
|
||||
INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const;
|
||||
|
||||
INLINE PointerToArray<Element> __deepcopy__(PyObject *memo) const;
|
||||
};
|
||||
|
||||
template<>
|
||||
@ -81,6 +83,8 @@ public:
|
||||
|
||||
INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
|
||||
INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const;
|
||||
|
||||
INLINE ConstPointerToArray<Element> __deepcopy__(PyObject *memo) const;
|
||||
};
|
||||
|
||||
template<>
|
||||
|
@ -4472,6 +4472,20 @@ get_data4f(const unsigned char *pointer) {
|
||||
return _v4;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
const LVecBase4d &GeomVertexColumn::Packer_argb_packed::
|
||||
get_data4d(const unsigned char *pointer) {
|
||||
uint32_t dword = *(const uint32_t *)pointer;
|
||||
_v4d.set(GeomVertexData::unpack_abcd_b(dword),
|
||||
GeomVertexData::unpack_abcd_c(dword),
|
||||
GeomVertexData::unpack_abcd_d(dword),
|
||||
GeomVertexData::unpack_abcd_a(dword));
|
||||
_v4d /= 255.0;
|
||||
return _v4d;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ -4486,6 +4500,20 @@ set_data4f(unsigned char *pointer, const LVecBase4f &data) {
|
||||
(unsigned int)(min(max(data[2], 0.0f), 1.0f) * 255.0f));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void GeomVertexColumn::Packer_argb_packed::
|
||||
set_data4d(unsigned char *pointer, const LVecBase4d &data) {
|
||||
// when packing an argb, we want to make sure we cap the input values at 1
|
||||
// since going above one will cause the value to be truncated.
|
||||
*(uint32_t *)pointer = GeomVertexData::pack_abcd
|
||||
((unsigned int)(min(max(data[3], 0.0), 1.0) * 255.0),
|
||||
(unsigned int)(min(max(data[0], 0.0), 1.0) * 255.0),
|
||||
(unsigned int)(min(max(data[1], 0.0), 1.0) * 255.0),
|
||||
(unsigned int)(min(max(data[2], 0.0), 1.0) * 255.0));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ -4497,6 +4525,17 @@ get_data4f(const unsigned char *pointer) {
|
||||
return _v4;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
const LVecBase4d &GeomVertexColumn::Packer_rgba_uint8_4::
|
||||
get_data4d(const unsigned char *pointer) {
|
||||
_v4d.set((double)pointer[0], (double)pointer[1],
|
||||
(double)pointer[2], (double)pointer[3]);
|
||||
_v4d /= 255.0;
|
||||
return _v4d;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ -4508,6 +4547,17 @@ set_data4f(unsigned char *pointer, const LVecBase4f &data) {
|
||||
pointer[3] = (unsigned int)(min(max(data[3], 0.0f), 1.0f) * 255.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void GeomVertexColumn::Packer_rgba_uint8_4::
|
||||
set_data4d(unsigned char *pointer, const LVecBase4d &data) {
|
||||
pointer[0] = (unsigned int)(min(max(data[0], 0.0), 1.0) * 255.0);
|
||||
pointer[1] = (unsigned int)(min(max(data[1], 0.0), 1.0) * 255.0);
|
||||
pointer[2] = (unsigned int)(min(max(data[2], 0.0), 1.0) * 255.0);
|
||||
pointer[3] = (unsigned int)(min(max(data[3], 0.0), 1.0) * 255.0);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -381,7 +381,9 @@ private:
|
||||
class Packer_argb_packed final : public Packer_color {
|
||||
public:
|
||||
virtual const LVecBase4f &get_data4f(const unsigned char *pointer);
|
||||
virtual const LVecBase4d &get_data4d(const unsigned char *pointer);
|
||||
virtual void set_data4f(unsigned char *pointer, const LVecBase4f &value);
|
||||
virtual void set_data4d(unsigned char *pointer, const LVecBase4d &value);
|
||||
|
||||
virtual const char *get_name() const {
|
||||
return "Packer_argb_packed";
|
||||
@ -391,7 +393,9 @@ private:
|
||||
class Packer_rgba_uint8_4 final : public Packer_color {
|
||||
public:
|
||||
virtual const LVecBase4f &get_data4f(const unsigned char *pointer);
|
||||
virtual const LVecBase4d &get_data4d(const unsigned char *pointer);
|
||||
virtual void set_data4f(unsigned char *pointer, const LVecBase4f &value);
|
||||
virtual void set_data4d(unsigned char *pointer, const LVecBase4d &value);
|
||||
|
||||
virtual const char *get_name() const {
|
||||
return "Packer_rgba_uint8_4";
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "pnmImage.h"
|
||||
#include "pfmFile.h"
|
||||
#include "asyncTask.h"
|
||||
#include "extension.h"
|
||||
|
||||
class TextureContext;
|
||||
class FactoryParams;
|
||||
@ -473,6 +474,8 @@ PUBLISHED:
|
||||
MAKE_PROPERTY(keep_ram_image, get_keep_ram_image, set_keep_ram_image);
|
||||
MAKE_PROPERTY(cacheable, is_cacheable);
|
||||
|
||||
EXTENSION(PT(Texture) __deepcopy__(PyObject *memo) const);
|
||||
|
||||
BLOCKING INLINE bool compress_ram_image(CompressionMode compression = CM_on,
|
||||
QualityLevel quality_level = QL_default,
|
||||
GraphicsStateGuardianBase *gsg = nullptr);
|
||||
@ -1122,6 +1125,7 @@ private:
|
||||
|
||||
static TypeHandle _type_handle;
|
||||
|
||||
friend class Extension<Texture>;
|
||||
friend class TextureContext;
|
||||
friend class PreparedGraphicsObjects;
|
||||
friend class TexturePool;
|
||||
|
@ -138,4 +138,32 @@ set_ram_image_as(PyObject *image, const std::string &provided_format) {
|
||||
Dtool_Raise_ArgTypeError(image, 0, "Texture.set_ram_image_as", "CPTA_uchar or buffer");
|
||||
}
|
||||
|
||||
/**
|
||||
* A special Python method that is invoked by copy.deepcopy(tex). This makes
|
||||
* sure that the copy has a unique copy of the RAM image.
|
||||
*/
|
||||
PT(Texture) Extension<Texture>::
|
||||
__deepcopy__(PyObject *memo) const {
|
||||
PT(Texture) copy = _this->make_copy();
|
||||
{
|
||||
Texture::CDWriter cdata(copy->_cycler, true);
|
||||
for (Texture::RamImage &image : cdata->_ram_images) {
|
||||
if (image._image.get_ref_count() > 1) {
|
||||
PTA_uchar new_image;
|
||||
new_image.v() = image._image.v();
|
||||
image._image = std::move(new_image);
|
||||
}
|
||||
}
|
||||
{
|
||||
Texture::RamImage &image = cdata->_simple_ram_image;
|
||||
if (image._image.get_ref_count() > 1) {
|
||||
PTA_uchar new_image;
|
||||
new_image.v() = image._image.v();
|
||||
image._image = std::move(new_image);
|
||||
}
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
|
@ -32,6 +32,8 @@ public:
|
||||
void set_ram_image(PyObject *image, Texture::CompressionMode compression = Texture::CM_off,
|
||||
size_t page_size = 0);
|
||||
void set_ram_image_as(PyObject *image, const std::string &provided_format);
|
||||
|
||||
PT(Texture) __deepcopy__(PyObject *memo) const;
|
||||
};
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
|
@ -69,3 +69,63 @@ def test_cpta_float_pickle():
|
||||
data_pta2 = loads(dumps(data_pta, proto))
|
||||
assert tuple(data_pta2) == (1.0, 2.0, 3.0)
|
||||
assert data_pta2.get_data() == data_pta.get_data()
|
||||
|
||||
|
||||
def test_pta_float_copy():
|
||||
from panda3d.core import PTA_float
|
||||
from copy import copy
|
||||
|
||||
null_pta = PTA_float()
|
||||
assert copy(null_pta).is_null()
|
||||
|
||||
empty_pta = PTA_float([])
|
||||
empty_pta_copy = copy(empty_pta)
|
||||
assert not empty_pta_copy.is_null()
|
||||
assert len(empty_pta_copy) == 0
|
||||
assert empty_pta_copy.get_ref_count() == 2
|
||||
|
||||
data_pta = PTA_float([1.0, 2.0, 3.0])
|
||||
data_pta_copy = copy(data_pta)
|
||||
assert not data_pta_copy.is_null()
|
||||
assert data_pta_copy.get_ref_count() == 2
|
||||
assert tuple(data_pta_copy) == (1.0, 2.0, 3.0)
|
||||
|
||||
|
||||
def test_pta_float_deepcopy():
|
||||
from panda3d.core import PTA_float
|
||||
from copy import deepcopy
|
||||
|
||||
null_pta = PTA_float()
|
||||
assert deepcopy(null_pta).is_null()
|
||||
|
||||
empty_pta = PTA_float([])
|
||||
empty_pta_copy = deepcopy(empty_pta)
|
||||
assert not empty_pta_copy.is_null()
|
||||
assert len(empty_pta_copy) == 0
|
||||
assert empty_pta_copy.get_ref_count() == 1
|
||||
|
||||
data_pta = PTA_float([1.0, 2.0, 3.0])
|
||||
data_pta_copy = deepcopy(data_pta)
|
||||
assert not data_pta_copy.is_null()
|
||||
assert data_pta_copy.get_ref_count() == 1
|
||||
assert tuple(data_pta_copy) == (1.0, 2.0, 3.0)
|
||||
|
||||
|
||||
def test_cpta_float_deepcopy():
|
||||
from panda3d.core import PTA_float, CPTA_float
|
||||
from copy import deepcopy
|
||||
|
||||
null_pta = CPTA_float(PTA_float())
|
||||
assert deepcopy(null_pta).is_null()
|
||||
|
||||
empty_pta = CPTA_float([])
|
||||
empty_pta_copy = deepcopy(empty_pta)
|
||||
assert not empty_pta_copy.is_null()
|
||||
assert len(empty_pta_copy) == 0
|
||||
assert empty_pta_copy.get_ref_count() == 1
|
||||
|
||||
data_pta = CPTA_float([1.0, 2.0, 3.0])
|
||||
data_pta_copy = deepcopy(data_pta)
|
||||
assert not data_pta_copy.is_null()
|
||||
assert data_pta_copy.get_ref_count() == 1
|
||||
assert tuple(data_pta_copy) == (1.0, 2.0, 3.0)
|
||||
|
@ -134,3 +134,26 @@ def test_texture_clear_half():
|
||||
assert col.y == -inf
|
||||
assert col.z == -inf
|
||||
assert math.isnan(col.w)
|
||||
|
||||
|
||||
def test_texture_deepcopy():
|
||||
from copy import deepcopy
|
||||
|
||||
empty_tex = Texture("empty-texture")
|
||||
empty_tex.setup_2d_texture(16, 16, Texture.T_unsigned_byte, Texture.F_rgba)
|
||||
assert not empty_tex.has_ram_image()
|
||||
empty_tex2 = deepcopy(empty_tex)
|
||||
assert empty_tex2.name == empty_tex.name
|
||||
assert not empty_tex2.has_ram_image()
|
||||
|
||||
tex = Texture("texture")
|
||||
tex.setup_2d_texture(16, 16, Texture.T_unsigned_byte, Texture.F_rgba)
|
||||
img = tex.make_ram_image()
|
||||
assert tex.has_ram_image()
|
||||
assert img.get_ref_count() == 2
|
||||
|
||||
tex2 = deepcopy(tex)
|
||||
assert tex2.name == tex.name
|
||||
assert tex2.has_ram_image()
|
||||
img2 = tex2.get_ram_image()
|
||||
assert img2.get_ref_count() == 2
|
||||
|
Loading…
x
Reference in New Issue
Block a user