Browse Source

refact image and texture constructions

Bastien Sevajol 6 years ago
parent
commit
58925644de
3 changed files with 160 additions and 62 deletions
  1. 81 61
      synergine2_cocos2d/actor.py
  2. 9 0
      synergine2_xyz/exception.py
  3. 70 1
      synergine2_xyz/image.py

+ 81 - 61
synergine2_cocos2d/actor.py View File

@@ -11,6 +11,7 @@ from cocos import collision_model
11 11
 from cocos import euclid
12 12
 
13 13
 from synergine2.config import Config
14
+from synergine2.log import get_logger
14 15
 from synergine2.simulation import Subject
15 16
 from synergine2_cocos2d.animation import AnimatedInterface
16 17
 from synergine2_cocos2d.util import PathManager
@@ -38,14 +39,11 @@ class Actor(AnimatedInterface, cocos.sprite.Sprite):
38 39
         assert config, "Config is a required parameter"
39 40
         self.config = config
40 41
         self.path_manager = PathManager(config.resolve('global.include_path.graphics'))
41
-        default_image_path = self.build_default_image(
42
-            subject.id,
43
-            self.path_manager.path(image_path),
44
-        )
45
-        image = pyglet.image.load(os.path.abspath(default_image_path))
46 42
         self.animation_images = {}  # type: typing.Dict[str, typing.List[pyglet.image.TextureRegion]]  # nopep8
43
+
44
+        default_texture = self._get_default_image_texture()
47 45
         super().__init__(
48
-            image,
46
+            default_texture,
49 47
             position,
50 48
             rotation,
51 49
             scale,
@@ -54,45 +52,56 @@ class Actor(AnimatedInterface, cocos.sprite.Sprite):
54 52
             anchor,
55 53
             **kwargs
56 54
         )
55
+
56
+        self.logger = get_logger('Actor', config)
57
+
57 58
         self.subject = subject
58 59
         self.cshape = None  # type: collision_model.AARectShape
59 60
         self.update_cshape()
60
-        self.build_animation_images(subject.id)
61
-        self.current_image = image
61
+        self.default_texture = default_texture
62 62
         self.need_update_cshape = False
63 63
         self.properties = properties or {}
64 64
         self._freeze = False
65 65
 
66
+        self.animation_textures_cache = {}  # type: typing.Dict[str, typing.List[pyglet.image.TextureRegion]]  # nopep8
67
+        self.mode_texture_cache = {}  # type: typing.Dict[str, pyglet.image.TextureRegion]  # nopep8
68
+
66 69
         self.default_image_path = image_path
67
-        self.image_cache = self.get_image_cache_manager()
68
-        self.image_cache.build()
70
+        self.image_cache_manager = self.get_image_cache_manager()
71
+        self.image_cache_manager.build()
72
+
73
+        self.build_textures_cache()
69 74
 
70 75
     def get_image_cache_manager(self) -> ImageCacheManager:
71 76
         return ImageCacheManager(self, self.config)
72 77
 
73
-    def build_default_image(self, subject_id: int, base_image_path: str) -> str:
78
+    def _get_default_image_texture(self) -> pyglet.image.AbstractImage:
79
+        """
80
+        Build and return teh default actor image texture.
81
+        :return: default actor image texture
82
+        """
74 83
         cache_dir = self.config.resolve('global.cache_dir_path')
75
-        with open(base_image_path, 'rb') as base_image_file:
76
-            base_image = Image.open(base_image_file)
77
-
78
-            for default_appliable_image in self.get_default_appliable_images():
79
-                base_image.paste(
80
-                    default_appliable_image,
81
-                    (0, 0),
82
-                    default_appliable_image,
83
-                )
84
+        mode = self.get_default_mode()
85
+        image_path = self.get_mode_image_path(mode)
86
+        final_image_path = self.path_manager.path(image_path)
87
+
88
+        image = Image.open(final_image_path)
89
+        image_path = '{}_default_image.png'
90
+        final_image_path = os.path.join(cache_dir, image_path)
91
+        image.save(final_image_path)
84 92
 
85
-            # FIXME NOW: nom des image utilise au dessus
86
-            final_name = '_'.join([
87
-                str(subject_id),
88
-                ntpath.basename(base_image_path),
89
-            ])
90
-            final_path = os.path.join(cache_dir, final_name)
91
-            base_image.save(final_path)
93
+        return pyglet.image.load(final_image_path)
92 94
 
93
-        return final_path
95
+    def get_default_mode(self) -> str:
96
+        raise NotImplementedError()
94 97
 
95
-    def get_default_appliable_images(self) -> typing.List[Image.Image]:
98
+    def get_mode_image_path(self, mode: str) -> str:
99
+        raise NotImplementedError()
100
+
101
+    def get_modes(self) -> typing.List[str]:
102
+        raise NotImplementedError()
103
+
104
+    def get_mode_appliable_images(self, mode: str) -> typing.List[Image.Image]:
96 105
         return []
97 106
 
98 107
     def get_animation_appliable_images(
@@ -128,48 +137,59 @@ class Actor(AnimatedInterface, cocos.sprite.Sprite):
128 137
         self.position = new_position
129 138
         self.cshape.center = new_position  # Note: if remove: strange behaviour: drag change actor position with anomaly
130 139
 
131
-    def build_animation_images(self, subject_id: int) -> None:
132
-        """
133
-        Fill self.animation_images with self.animation_image_paths
134
-        :return: None
135
-        """
136
-        cache_dir = self.config.resolve('global.cache_dir_path')
137
-        for animation_name, animation_image_paths in self.animation_image_paths.items():
138
-            self.animation_images[animation_name] = []
139
-            for i, animation_image_path in enumerate(animation_image_paths):
140
-                final_image_path = self.path_manager.path(animation_image_path)
141
-                final_image = Image.open(final_image_path)
140
+    def build_textures_cache(self) -> None:
141
+        self.build_modes_texture_cache()
142
+        self.build_animation_textures_cache()
142 143
 
143
-                for appliable_image in self.get_animation_appliable_images(
144
-                    animation_name,
145
-                    i,
146
-                ):
147
-                    final_image.paste(
148
-                        appliable_image,
144
+    def build_modes_texture_cache(self) -> None:
145
+        cache_dir = self.config.resolve('global.cache_dir_path')
146
+        for mode in self.get_modes():
147
+            mode_image_path = self.path_manager.path(self.get_mode_image_path(mode))
148
+            with open(mode_image_path, 'rb') as base_image_file:
149
+                base_image = Image.open(base_image_file)
150
+
151
+                for default_appliable_image in self.get_mode_appliable_images(mode):
152
+                    base_image.paste(
153
+                        default_appliable_image,
149 154
                         (0, 0),
150
-                        appliable_image,
155
+                        default_appliable_image,
151 156
                     )
152 157
 
153
-                # FIXME NOW: nom des image utilise au dessus
154
-                final_name = '_'.join([
155
-                    str(subject_id),
156
-                    ntpath.basename(final_image_path),
157
-                ])
158
+                final_name = '{}_mode_{}.png'.format(
159
+                    str(self.subject.id),
160
+                    mode,
161
+                )
158 162
                 final_path = os.path.join(cache_dir, final_name)
163
+                base_image.save(final_path)
164
+                self.mode_texture_cache[mode] = pyglet.image.load(final_path)
159 165
 
160
-                final_image.save(final_path)
161
-
162
-                self.animation_images[animation_name].append(
163
-                    pyglet.image.load(
164
-                        final_path,
165
-                    )
166
+    def build_animation_textures_cache(self) -> None:
167
+        cache_dir = self.config.resolve('global.cache_dir_path')
168
+        for animation_name in self.animation_image_paths.keys():
169
+            texture_regions = []
170
+            animation_images = \
171
+                self.image_cache_manager.animation_cache.get_animation_images(
172
+                    animation_name)
173
+
174
+            for i, animation_image in enumerate(animation_images):
175
+                cache_image_name = '{}_animation_{}_{}.png'.format(
176
+                    self.subject.id,
177
+                    animation_name,
178
+                    i,
166 179
                 )
180
+                cache_image_path = os.path.join(cache_dir, cache_image_name)
181
+                animation_image.save(cache_image_path)
182
+                self.animation_textures_cache.setdefault(animation_name, [])\
183
+                    .append(pyglet.image.load(cache_image_path))
167 184
 
168
-    def get_images_for_animation(self, animation_name: str) -> typing.List[pyglet.image.TextureRegion]:
169
-        return self.animation_images.get(animation_name)
185
+    def get_images_for_animation(
186
+        self,
187
+        animation_name: str,
188
+    ) -> typing.List[pyglet.image.TextureRegion]:
189
+        return self.animation_textures_cache[animation_name]
170 190
 
171 191
     def get_inanimate_image(self) -> pyglet.image.TextureRegion:
172
-        return self.current_image
192
+        return self.default_texture
173 193
 
174 194
     def update_image(self, new_image: pyglet.image.TextureRegion):
175 195
         if self._freeze:

+ 9 - 0
synergine2_xyz/exception.py View File

@@ -1 +1,10 @@
1 1
 # coding: utf-8
2
+from synergine2.exceptions import SynergineException
3
+
4
+
5
+class UnknownAnimationIndex(SynergineException):
6
+    pass
7
+
8
+
9
+class UnknownAnimation(SynergineException):
10
+    pass

+ 70 - 1
synergine2_xyz/image.py View File

@@ -1,16 +1,61 @@
1 1
 # coding: utf-8
2 2
 import typing
3 3
 
4
+from PIL import Image
5
+
4 6
 from synergine2.config import Config
7
+from synergine2_cocos2d.util import PathManager
8
+from synergine2_xyz.exception import UnknownAnimationIndex
9
+from synergine2_xyz.exception import UnknownAnimation
5 10
 
6 11
 if typing.TYPE_CHECKING:
7 12
     from synergine2_cocos2d.actor import Actor
8 13
 
14
+
9 15
 class ImageCache(object):
10 16
     def __init__(self) -> None:
11 17
         self.cache = {}
12 18
 
13 19
 
20
+class AnimationImageCache(object):
21
+    def __init__(self) -> None:
22
+        self.cache = {}
23
+
24
+    def add(
25
+        self,
26
+        animation_name: str,
27
+        image: Image.Image,
28
+    ) -> None:
29
+        self.cache.setdefault(animation_name, []).append(image)
30
+
31
+    def get(
32
+        self,
33
+        animation_name: str,
34
+        image_position: int,
35
+    ) -> Image.Image:
36
+        try:
37
+            return self.cache[animation_name][image_position]
38
+        except KeyError:
39
+            raise Exception('TODO')
40
+        except IndexError:
41
+            raise UnknownAnimationIndex(
42
+                'Unknown animation index "{}" for animation "{}"'.format(
43
+                    image_position,
44
+                    animation_name,
45
+                ),
46
+            )
47
+
48
+    def get_animation_images(self, animation_name: str) -> typing.List[Image.Image]:
49
+        try:
50
+            return self.cache[animation_name]
51
+        except KeyError:
52
+            raise UnknownAnimation(
53
+                'Unknown animation "{}"'.format(
54
+                    animation_name,
55
+                ),
56
+            )
57
+
58
+
14 59
 class ImageCacheManager(object):
15 60
     def __init__(
16 61
         self,
@@ -20,5 +65,29 @@ class ImageCacheManager(object):
20 65
         self.config = config
21 66
         self.actor = actor
22 67
 
68
+        self.path_manager = PathManager(config.resolve('global.include_path.graphics'))
69
+        self.animation_cache = AnimationImageCache()
70
+
23 71
     def build(self) -> None:
24
-        pass
72
+        self.build_animation_cache()
73
+
74
+    def build_animation_cache(self) -> None:
75
+        cache_dir = self.config.resolve('global.cache_dir_path')
76
+        animation_images = self.actor.animation_image_paths.items()
77
+
78
+        for animation_name, animation_image_paths in animation_images:
79
+            for i, animation_image_path in enumerate(animation_image_paths):
80
+                final_image_path = self.path_manager.path(animation_image_path)
81
+                final_image = Image.open(final_image_path)
82
+
83
+                for appliable_image in self.actor.get_animation_appliable_images(
84
+                    animation_name,
85
+                    i,
86
+                ):
87
+                    final_image.paste(
88
+                        appliable_image,
89
+                        (0, 0),
90
+                        appliable_image,
91
+                    )
92
+
93
+                self.animation_cache.add(animation_name, final_image)