View difference between Paste ID: 4DTsBXwg and TWRh5qDF
SHOW: | | - or go back to the newest paste.
1
#!/usr/bin/env python
2
#
3
# Copyright (c) 2011-2012 Wiktor Starzyk, Faisal Z. Qureshi
4
#
5
# This file is part of the Virtual Vision Simulator.
6
#
7
# The Virtual Vision Simulator is free software: you can 
8
# redistribute it and/or modify it under the terms 
9
# of the GNU General Public License as published by
10
# the Free Software Foundation, either version 3 of the License, 
11
# or (at your option) any later version.
12
#
13
# The Virtual Vision Simulator is distributed in the hope 
14
# that it will be useful, but WITHOUT ANY WARRANTY; 
15
# without even the implied warranty of MERCHANTABILITY or 
16
# FITNESS FOR A PARTICULAR PURPOSE.  See the
17
# GNU General Public License for more details.
18
#
19
# You should have received a copy of the GNU General Public License
20
# along with the Virtual Vision Simulator.  
21
# If not, see <http://www.gnu.org/licenses/>.
22
#
23
24
25
import sys, os
26
import logging
27
28
from math import tan, radians
29
30
from direct.showbase.ShowBase import ShowBase
31
from direct.gui.OnscreenText import OnscreenText as OST
32
from direct.task import Task
33
34
from pandac.PandaModules import AntialiasAttrib, ClockObject, TextNode
35
from pandac.PandaModules import Vec3, VBase4
36
from pandac.PandaModules import Camera, OrthographicLens
37
from pandac.PandaModules import loadPrcFileData
38
from pandac.PandaModules import WindowProperties 
39
40
from builders.object_builder import ObjectBuilder
41
from builders.pedestrian_builder import PedestrianBuilder
42
from builders.light_builder import LightBuilder
43
from builders.camera_builder import PandaCameraBuilder
44
from file_io.scene_file import *
45
from file_io.pedestrian_file import *
46
47
from simulator.model import Model
48
from simulator.model import DIRECTIONALLIGHT
49
from simulator.panda3d.controller import Controller
50
51
WIDTH = 1024
52
HEIGHT = 768
53
54
loadPrcFileData("", "win-size %s %s" % (WIDTH, HEIGHT))
55
loadPrcFileData("", "texture-anisotropic-degree 10")
56
57
LIGHTS_PER_OBJECT = 4
58
59
PTZ_CAMERA = 1
60
WIDE_FOV_CAMERA = 2
61
62
MANUAL_CAMERA = False
63
64
65
def Length(a, b):
66
    ax, ay, az = a
67
    bx, by, bz = b
68
    length = (ax-bx) ** 2 + (ay-by) ** 2
69
    return length
70
71
72
class VirtualWorld(ShowBase): 
73
74
    def __init__(self, scene_file, pedestrian_file, dir, mode):
75
        ShowBase.__init__(self)
76
77
        self.globalClock = ClockObject.getGlobalClock()
78
        self.globalClock.setMode(ClockObject.MSlave)
79
        
80
        self.directory = dir
81
        self.model = Model(dir)
82
        self.loadScene(scene_file)
83
        self.loadPedestrians(pedestrian_file)
84
        
85
        #self.cam_label = OST("Top Down", pos=(0, 0.95), fg=(1,1,1,1), 
86
        #                     scale=0.05, mayChange=True)
87
        #self.time_label = OST("Time: 0.0", pos=(-1.3, 0.95), fg=(1,1,1,1), 
88
        #                      scale=0.06, mayChange=True, align=TextNode.ALeft)
89
                                       
90
        #self.accept("arrow_right", self.changeCamera, [1])
91
        #self.accept("arrow_left", self.changeCamera, [-1])
92
        self.accept("escape", self.exit)
93
        self.accept("aspectRatioChanged", self.setAspectRatio)
94
        self.accept("window-event", self.windowChanged)
95
        
96
        #base.disableMouse()
97
        lens = OrthographicLens()
98
        lens.setFilmSize(1550, 1000)
99
        
100
        self.default_camera = render.attachNewNode(Camera("top down"))
101
        self.default_camera.node().setLens(lens)
102
        self.default_camera.setPosHpr(Vec3( -75, 0, 2200), Vec3(0, -90, 0))
103
        
104
        self.display_regions = []
105
        self.display_regions.append(base.win.makeDisplayRegion(0, 0.33, 0.5, 1))
106
        self.display_regions.append(base.win.makeDisplayRegion(0.33, 0.66, 0.5, 1))
107
        self.display_regions.append(base.win.makeDisplayRegion(0.66, 1, 0.5, 1))
108
        self.display_regions.append(base.win.makeDisplayRegion(0, 0.33, 0, 0.5))
109
        self.display_regions.append(base.win.makeDisplayRegion(0.33, 0.66, 0, 0.5))
110
        self.display_regions.append(base.win.makeDisplayRegion(0.66, 1, 0, 0.5))
111
        
112
        for i in range(0, len(self.display_regions)):
113
	    self.assign_display_region_to_camera(self.display_regions[i], i)
114
115
        #self.setCamera(0)
116
117
        self.controller = Controller(self, mode)
118
        self.taskMgr.add(self.updateCameraModules, "Update Camera Modules", 80)
119
        
120
        self.globalClock.setFrameTime(0.0)
121
        self.width = WIDTH
122
        self.height = HEIGHT
123
124
        props = WindowProperties( ) 
125
        props.setTitle( 'Virtual Vision Simulator' ) 
126
        base.win.requestProperties( props )
127
128
129
    def assign_display_region_to_camera(self, display_region, camera_number):
130
        
131
        display_region.setClearColor(VBase4(0, 0, 0, 1))
132
        display_region.setClearColorActive(True)
133
        display_region.setClearDepthActive(True)
134
        if camera_number == 0:
135
            display_region.setCamera(self.default_camera)
136
        else:
137
            camera_list = self.model.getCameraList()
138
            index = camera_number - 1
139
            if index < len(camera_list):
140
                camera = camera_list[index]
141
                camera_np = camera.getCameraNode()
142
                display_region.setCamera(camera_np)
143
        
144
        
145
    def getModel(self):
146
        """
147
        Returns the model that stores all of the cameras, pedestrians and 
148
        static objects in the scene.
149
        """
150
        return self.model
151
152
153
    def getController(self):
154
        """
155
        Returns a controller that is used to control the world time.
156
        """
157
        return self.controller
158
159
160
    def getTime(self):
161
        """
162
        Returns the current time in the world.
163
        """
164
        return self.globalClock.getFrameTime()
165
166
167
    def loadScene(self, scene_file):
168
        """
169
        Loads the static objects that make up the scene. Also loads the lights
170
        that illuminate the scene and for performance implications, sets what 
171
        lights affect what objects.
172
        """
173
        if not os.path.exists(scene_file):
174
            logging.error("The path '%s' does not exist" % scene_file)
175
            sys.exit()
176
        light_builder = LightBuilder(self)
177
        object_builder = ObjectBuilder(self, self.directory)
178
        parser = SceneFileParser(self.model, object_builder, light_builder)
179
        parser.parse(scene_file)
180
        
181
        self.setUpLights()
182
183
184
    def setUpLights(self):
185
        # Set what lights illuminate what objects
186
        light_list = self.model.getLightList()
187
        static_objects = self.model.getObjectList()
188
        for object in static_objects:
189
            if object.hasLighting():
190
                model_root = object.getModel().getChildren()[0]
191
                children = model_root.getChildren()
192
                for child in children:
193
                    light_map = {}
194
                    for index, light in enumerate(light_list):
195
                        distance = Length(child.getPos(render), light.getPos())
196
                        half_fov = light.node().getLens().getFov()[0] / 2.0
197
                        height = light.getPos()[2]
198
                        radius = height * tan(radians(half_fov))
199
                        if distance > radius ** 2 + 2500 + 10:
200
                            continue
201
                        if distance not in light_map:
202
                            light_map[distance] = [index]
203
                        else:
204
                            light_map[distance].append(index)
205
206
                    sorted_lights = sorted(light_map.keys())
207
                    light_count = 0
208
                    for key in sorted_lights:
209
                        for i in light_map[key]:
210
                            child.setLight(light_list[i])
211
                            light_count += 1
212
                            if light_count > LIGHTS_PER_OBJECT:
213
                                break
214
                        if light_count > LIGHTS_PER_OBJECT:
215
                            break
216
                    child.flattenStrong()
217
        
218
        # Apply a directional light to the static models        
219
        light_list = self.model.getLightList(DIRECTIONALLIGHT)
220
        if light_list:
221
            for object in static_objects:
222
                if object.hasLighting():
223
                    model_root = object.getModel().getChildren()[0]
224
                    model_root.setLight(light_list[0])
225
226
        render.setShaderAuto()
227
        render.setAntialias(AntialiasAttrib.MLine)
228
229
230
    def loadPedestrians(self, pedestrian_file):
231
        """Loads the pedestrians into the scene."""
232
        if not os.path.exists(pedestrian_file):
233
            logging.error("The path '%s' does not exist" % pedestrian_file)
234
            sys.exit()
235
        pedestrian_builder = PedestrianBuilder(self, "../media/characters/")
236
        parser = PedestrianFileParser(self.model, pedestrian_builder)
237
        parser.parse("../media/characters/pedestrians.xml")
238
        parser.parse(pedestrian_file)
239
240
241
    def addCamera(self, config):
242
        """
243
        This method is used to add a new panda camera to the world. The panda
244
        camera is returned so that it can be linked with a camera module.
245
        """
246
        type = config.type
247
        cam_builder = PandaCameraBuilder(self)
248
        if type == WIDE_FOV_CAMERA:
249
            pass
250
        else:
251
            camera = cam_builder.buildPandaPTZCamera(config)
252
            self.model.addCamera(camera)
253
        return camera
254
255
256
    def setAspectRatio(self):
257
        """
258
        This method is called when the aspect ratio of the window changes.
259
        It updates the aspect ratios of all the cameras.
260
        """
261
        width = base.win.getXSize()
262
        height = base.win.getYSize()
263
        ratio = self.camLens.getAspectRatio()
264
        camera_list = self.model.getCameraList()
265
        for camera in camera_list:
266
            camera.setAspectRatio(ratio)
267
            camera.setImageSize(width, height)
268
269
        self.default_camera.node().getLens().setAspectRatio(ratio)
270
        r =  width / float(height)
271
        #self.time_label.setPos(-r, 0.95)
272
273
274
    def changeCamera(self, num):
275
        """
276
        This method is used to toggle the camera that is viewed in the main 
277
        window. Typically num is either 1 or -1 denoting whether to toggle up
278
        or down the camera list.
279
        """
280
        number = self.cur_camera + 1 + num
281
        num_cameras = len(self.model.getCameraList())
282
        if number > num_cameras:
283
            number = 0
284
        elif number < 0:
285
            number = num_cameras
286
        self.setCamera(number)
287
288
289
    def setCamera(self, num):
290
        """
291
        This method sets which cameras view is shown in the panda3d window.
292
        """
293
        if MANUAL_CAMERA:
294
            self.cur_camera = num -1
295
            return
296
        
297
        self.display_region.setClearColor(VBase4(0, 0, 0, 1))
298
        self.display_region.setClearColorActive(True)
299
        self.display_region.setClearDepthActive(True)
300
        if num == 0:
301
            self.cur_camera = -1
302
            self.display_region.setCamera(self.default_camera)
303
            self.cam_label.setText("Top Down")
304
        else:
305
            camera_list = self.model.getCameraList()
306
            index = num - 1
307
            if index < len(camera_list):
308
                self.cur_camera = index
309
                camera = camera_list[index]
310
                camera_np = camera.getCameraNode()
311
                self.display_region.setCamera(camera_np)
312
                name = camera.getName()
313
                status_label = camera.getStatusLabel()
314
                label = "%s: %s" %(name, status_label)
315
                self.cam_label.setText(label)
316
317
318
    def step(self, increment):
319
        """
320
        This method updates the world by one time step.
321
        """
322
        if increment:
323
            new_time = self.globalClock.getFrameTime() + increment
324
        else:
325
            new_time = self.globalClock.getRealTime()
326
            
327
        self.globalClock.setFrameTime(new_time)
328
        #self.time_label.setText("Time: %.2f" % new_time)
329
        
330
        self.updateActors()
331
        self.updateCameras()
332
333
334
    def updateActors(self):
335
        """
336
        This method updates the pedestrians in the scene by calling their update
337
        functions.
338
        """
339
        pedestrians = self.model.getPedestrianList()
340
        time = self.getTime()
341
        for pedestrian in pedestrians:
342
            if pedestrian.isActive(time):
343
                pedestrian.update(time)
344
345
346
    def updateCameras(self):
347
        """
348
        This method updates the panda cameras which are used to provide the
349
        higher level camera modules with rendered images of the scene. There
350
        is one panda camera for each camera module.
351
        """
352
        time = self.getTime()
353
        camera_list = self.model.getCameraList()
354
        for camera in camera_list:
355
            camera.update(time)
356
        
357
        """if self.cur_camera != -1:
358
            cur_camera = camera_list[self.cur_camera]
359
            if cur_camera.statusChanged():
360
                name = cur_camera.getName()
361
                status_label = cur_camera.getStatusLabel()
362
                label = "%s: %s" %(name, status_label)
363
                self.cam_label.setText(label)"""
364
365
366
    def updateCameraModules(self, task):
367
        """
368
        This method updates the camera modules by calling their update function.
369
        This allows the camera modules to process messages and complete any
370
        tasks that were assigned to them.
371
        """
372
        time = self.getTime()
373
        for camera in self.model.getCameraModules():
374
            camera.update(time)
375
        return Task.cont
376
377
378
    def windowChanged(self, window):
379
        """
380
        This function is called when the window is modified. It updates the
381
        image size used by the cameras when getting the rendered image from the 
382
        texture.
383
        """
384
        wp = window.getProperties()
385
        width = wp.getXSize()
386
        height = wp.getYSize()
387
        if width != self.width or height != self.height:
388
            self.width = width
389
            self.height = height
390
            camera_list = self.model.getCameraList()
391
            for camera in camera_list:
392
                camera.setImageSize(width, height)
393
        self.windowEvent(window)
394
395
396
    def exit(self):
397
        sys.exit()