Changeset 23190 in osm for applications/editors/josm/plugins/wms-turbo-challenge2
- Timestamp:
- 2010-09-15T18:54:18+02:00 (15 years ago)
- Location:
- applications/editors/josm/plugins/wms-turbo-challenge2/src/wmsturbochallenge
- Files:
-
- 4 edited
-
EngineSound.java (modified) (1 diff)
-
FakeMapView.java (modified) (1 diff)
-
GameWindow.java (modified) (1 diff)
-
WMSRacer.java (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/wms-turbo-challenge2/src/wmsturbochallenge/EngineSound.java
r19990 r23190 17 17 18 18 class engine { 19 public engine() {20 rpm = 0.0;21 }19 public engine() { 20 rpm = 0.0; 21 } 22 22 23 public void start() {24 rpm = 0.3;25 speed = 0.0;26 n = 0;23 public void start() { 24 rpm = 0.3; 25 speed = 0.0; 26 n = 0; 27 27 28 if (output != null)29 stop();28 if (output != null) 29 stop(); 30 30 31 AudioFormat output_format =32 new AudioFormat(S_RATE, 16, 1, true, true);33 DataLine.Info info =34 new DataLine.Info(SourceDataLine.class, output_format);31 AudioFormat output_format = 32 new AudioFormat(S_RATE, 16, 1, true, true); 33 DataLine.Info info = 34 new DataLine.Info(SourceDataLine.class, output_format); 35 35 36 /* Get the data line, open it and initialise the device */37 try {38 output = (SourceDataLine) AudioSystem.getLine(info);39 output.open(output_format);40 output.start();41 frames_written = 0;42 reschedule(0);43 } catch (Exception e) {44 output = null;45 System.out.println("Audio not available: " +46 e.getClass().getSimpleName());47 }48 }36 /* Get the data line, open it and initialise the device */ 37 try { 38 output = (SourceDataLine) AudioSystem.getLine(info); 39 output.open(output_format); 40 output.start(); 41 frames_written = 0; 42 reschedule(0); 43 } catch (Exception e) { 44 output = null; 45 System.out.println("Audio not available: " + 46 e.getClass().getSimpleName()); 47 } 48 } 49 49 50 public void stop() {51 rpm = 0.0;52 n = 0;50 public void stop() { 51 rpm = 0.0; 52 n = 0; 53 53 54 if (output == null)55 return;54 if (output == null) 55 return; 56 56 57 tick.cancel();58 tick.purge();57 tick.cancel(); 58 tick.purge(); 59 59 60 output.stop();61 output.flush();62 output.close();63 output = null;64 }60 output.stop(); 61 output.flush(); 62 output.close(); 63 output = null; 64 } 65 65 66 public void set_speed(double speed) {67 /* This engine is equipped with an automatic gear box that68 * switches gears when the RPM becomes too high or too low. */69 double new_speed = Math.abs(speed);70 double accel = new_speed - this.speed;71 this.speed = new_speed;66 public void set_speed(double speed) { 67 /* This engine is equipped with an automatic gear box that 68 * switches gears when the RPM becomes too high or too low. */ 69 double new_speed = Math.abs(speed); 70 double accel = new_speed - this.speed; 71 this.speed = new_speed; 72 72 73 if (accel > 0.05)74 accel = 0.05;75 else if (accel < -0.05)76 accel = -0.05;77 rpm += accel;73 if (accel > 0.05) 74 accel = 0.05; 75 else if (accel < -0.05) 76 accel = -0.05; 77 rpm += accel; 78 78 79 if (accel > 0.0 && rpm > 1.0 + n * 0.2 && speed > 0.0) {80 rpm = 0.3 + n * 0.2;81 n ++;82 } else if (accel < 0.0 && rpm < 0.3) {83 if (n > 0) {84 rpm = 0.7 + n * 0.1;85 n --;86 } else87 rpm = 0.2;88 }89 if (speed < 2.0)90 n = 0;91 }79 if (accel > 0.0 && rpm > 1.0 + n * 0.2 && speed > 0.0) { 80 rpm = 0.3 + n * 0.2; 81 n ++; 82 } else if (accel < 0.0 && rpm < 0.3) { 83 if (n > 0) { 84 rpm = 0.7 + n * 0.1; 85 n --; 86 } else 87 rpm = 0.2; 88 } 89 if (speed < 2.0) 90 n = 0; 91 } 92 92 93 public boolean is_on() {94 return output != null;95 }93 public boolean is_on() { 94 return output != null; 95 } 96 96 97 protected double speed;98 protected double rpm;99 protected int n;97 protected double speed; 98 protected double rpm; 99 protected int n; 100 100 101 protected SourceDataLine output = null;102 protected long frames_written;103 protected Timer tick = new Timer();101 protected SourceDataLine output = null; 102 protected long frames_written; 103 protected Timer tick = new Timer(); 104 104 105 /* Audio parameters. */106 protected static final int S_RATE = 44100;107 protected static final int MIN_BUFFER = 4096;108 protected static final double volume = 0.3;105 /* Audio parameters. */ 106 protected static final int S_RATE = 44100; 107 protected static final int MIN_BUFFER = 4096; 108 protected static final double volume = 0.3; 109 109 110 protected class audio_task extends TimerTask {111 public void run() {112 if (output == null)113 return;110 protected class audio_task extends TimerTask { 111 public void run() { 112 if (output == null) 113 return; 114 114 115 /* If more than a two buffers left to play,116 * reschedule and try to wake up closer to the117 * end of already written data. */118 long frames_current = output.getLongFramePosition();119 if (frames_current < frames_written - MIN_BUFFER * 2) {120 reschedule(frames_current);121 return;122 }115 /* If more than a two buffers left to play, 116 * reschedule and try to wake up closer to the 117 * end of already written data. */ 118 long frames_current = output.getLongFramePosition(); 119 if (frames_current < frames_written - MIN_BUFFER * 2) { 120 reschedule(frames_current); 121 return; 122 } 123 123 124 /* Build a new buffer */125 /* double freq = 20 * Math.pow(1.3, rpm * 5.0); */126 double freq = (rpm - 0.1) * 160.0;127 int wavelen = (int) (S_RATE / freq);128 int bufferlen = MIN_BUFFER - (MIN_BUFFER % wavelen) +129 wavelen;130 int value = (int) (0x7fff * volume);124 /* Build a new buffer */ 125 /* double freq = 20 * Math.pow(1.3, rpm * 5.0); */ 126 double freq = (rpm - 0.1) * 160.0; 127 int wavelen = (int) (S_RATE / freq); 128 int bufferlen = MIN_BUFFER - (MIN_BUFFER % wavelen) + 129 wavelen; 130 int value = (int) (0x7fff * volume); 131 131 132 bufferlen *= 2;133 byte[] buffer = new byte[bufferlen];134 for (int b = 0; b < bufferlen; ) {135 int j;136 for (j = wavelen / 2; j > 0; j --) {137 buffer[b ++] = (byte) (value >> 8);138 buffer[b ++] = (byte) (value & 0xff);139 }140 value = 0x10000 - value;141 for (j = wavelen - wavelen / 2; j > 0; j --) {142 buffer[b ++] = (byte) (value >> 8);143 buffer[b ++] = (byte) (value & 0xff);144 }145 value = 0x10000 - value;146 }132 bufferlen *= 2; 133 byte[] buffer = new byte[bufferlen]; 134 for (int b = 0; b < bufferlen; ) { 135 int j; 136 for (j = wavelen / 2; j > 0; j --) { 137 buffer[b ++] = (byte) (value >> 8); 138 buffer[b ++] = (byte) (value & 0xff); 139 } 140 value = 0x10000 - value; 141 for (j = wavelen - wavelen / 2; j > 0; j --) { 142 buffer[b ++] = (byte) (value >> 8); 143 buffer[b ++] = (byte) (value & 0xff); 144 } 145 value = 0x10000 - value; 146 } 147 147 148 frames_written +=149 output.write(buffer, 0, bufferlen) / 2;148 frames_written += 149 output.write(buffer, 0, bufferlen) / 2; 150 150 151 reschedule(frames_current);152 }153 }151 reschedule(frames_current); 152 } 153 } 154 154 155 protected void reschedule(long frames) {156 /* Send a new buffer as close to the end of the157 * currently playing buffer as possible (aim at158 * about half into the last frame). */159 long delay = (frames_written - frames - MIN_BUFFER / 2) *160 1000 / S_RATE;161 if (delay < 0)162 delay = 0;163 tick.schedule(new audio_task(), delay);164 }155 protected void reschedule(long frames) { 156 /* Send a new buffer as close to the end of the 157 * currently playing buffer as possible (aim at 158 * about half into the last frame). */ 159 long delay = (frames_written - frames - MIN_BUFFER / 2) * 160 1000 / S_RATE; 161 if (delay < 0) 162 delay = 0; 163 tick.schedule(new audio_task(), delay); 164 } 165 165 } -
applications/editors/josm/plugins/wms-turbo-challenge2/src/wmsturbochallenge/FakeMapView.java
r21477 r23190 26 26 27 27 class fake_map_view extends MapView { 28 public ProjectionBounds view_bounds;29 public MapView parent;28 public ProjectionBounds view_bounds; 29 public MapView parent; 30 30 31 public Graphics2D graphics;32 public BufferedImage ground_image;33 public int ground_width = -1;34 public int ground_height = -1;35 public double scale;36 public double max_east_west;31 public Graphics2D graphics; 32 public BufferedImage ground_image; 33 public int ground_width = -1; 34 public int ground_height = -1; 35 public double scale; 36 public double max_east_west; 37 37 38 public fake_map_view(MapView parent, double scale) {39 super(null); //TODO MapView constructor contains registering listeners and other code, that probably shouldn't be called in fake map view40 this.parent = parent;41 this.scale = scale;38 public fake_map_view(MapView parent, double scale) { 39 super(null); //TODO MapView constructor contains registering listeners and other code, that probably shouldn't be called in fake map view 40 this.parent = parent; 41 this.scale = scale; 42 42 43 ProjectionBounds parent_bounds = parent.getProjectionBounds();44 max_east_west =45 parent_bounds.max.east() - parent_bounds.min.east();46 }43 ProjectionBounds parent_bounds = parent.getProjectionBounds(); 44 max_east_west = 45 parent_bounds.max.east() - parent_bounds.min.east(); 46 } 47 47 48 public void setProjectionBounds(ProjectionBounds bounds) {49 view_bounds = bounds;48 public void setProjectionBounds(ProjectionBounds bounds) { 49 view_bounds = bounds; 50 50 51 if (bounds.max.east() - bounds.min.east() > max_east_west) {52 max_east_west = bounds.max.east() - bounds.min.east();51 if (bounds.max.east() - bounds.min.east() > max_east_west) { 52 max_east_west = bounds.max.east() - bounds.min.east(); 53 53 54 /* We need to set the parent MapView's bounds (i.e.55 * zoom level) to the same as ours max possible56 * bounds to avoid WMSLayer thinking we're zoomed57 * out more than we are or it'll pop up an annoying58 * "requested area is too large" popup.59 */60 EastNorth parent_center = parent.getCenter();61 parent.zoomTo(new ProjectionBounds(62 new EastNorth(63 parent_center.east() -64 max_east_west / 2,65 parent_center.north()),66 new EastNorth(67 parent_center.east() +68 max_east_west / 2,69 parent_center.north())));54 /* We need to set the parent MapView's bounds (i.e. 55 * zoom level) to the same as ours max possible 56 * bounds to avoid WMSLayer thinking we're zoomed 57 * out more than we are or it'll pop up an annoying 58 * "requested area is too large" popup. 59 */ 60 EastNorth parent_center = parent.getCenter(); 61 parent.zoomTo(new ProjectionBounds( 62 new EastNorth( 63 parent_center.east() - 64 max_east_west / 2, 65 parent_center.north()), 66 new EastNorth( 67 parent_center.east() + 68 max_east_west / 2, 69 parent_center.north()))); 70 70 71 /* Request again because NavigatableContent adds72 * a border just to be sure.73 */74 ProjectionBounds new_bounds =75 parent.getProjectionBounds();76 max_east_west =77 new_bounds.max.east() - new_bounds.min.east();78 }71 /* Request again because NavigatableContent adds 72 * a border just to be sure. 73 */ 74 ProjectionBounds new_bounds = 75 parent.getProjectionBounds(); 76 max_east_west = 77 new_bounds.max.east() - new_bounds.min.east(); 78 } 79 79 80 Point vmin = getPoint(bounds.min);81 Point vmax = getPoint(bounds.max);82 int w = vmax.x + 1;83 int h = vmin.y + 1;80 Point vmin = getPoint(bounds.min); 81 Point vmax = getPoint(bounds.max); 82 int w = vmax.x + 1; 83 int h = vmin.y + 1; 84 84 85 if (w <= ground_width && h <= ground_height) {86 graphics.setClip(0, 0, w, h);87 return;88 }85 if (w <= ground_width && h <= ground_height) { 86 graphics.setClip(0, 0, w, h); 87 return; 88 } 89 89 90 if (w > ground_width)91 ground_width = w;92 if (h > ground_height)93 ground_height = h;90 if (w > ground_width) 91 ground_width = w; 92 if (h > ground_height) 93 ground_height = h; 94 94 95 ground_image = new BufferedImage(ground_width,96 ground_height,97 BufferedImage.TYPE_INT_RGB);98 graphics = ground_image.createGraphics();99 graphics.setClip(0, 0, w, h);100 }95 ground_image = new BufferedImage(ground_width, 96 ground_height, 97 BufferedImage.TYPE_INT_RGB); 98 graphics = ground_image.createGraphics(); 99 graphics.setClip(0, 0, w, h); 100 } 101 101 102 public ProjectionBounds getProjectionBounds() {103 return view_bounds;104 }102 public ProjectionBounds getProjectionBounds() { 103 return view_bounds; 104 } 105 105 106 public Point getPoint(EastNorth p) {107 double x = p.east() - view_bounds.min.east();108 double y = view_bounds.max.north() - p.north();109 x /= this.scale;110 y /= this.scale;106 public Point getPoint(EastNorth p) { 107 double x = p.east() - view_bounds.min.east(); 108 double y = view_bounds.max.north() - p.north(); 109 x /= this.scale; 110 y /= this.scale; 111 111 112 return new Point((int) x, (int) y);113 }112 return new Point((int) x, (int) y); 113 } 114 114 115 public EastNorth getEastNorth(int x, int y) {116 return new EastNorth(117 view_bounds.min.east() + x * this.scale,118 view_bounds.min.north() - y * this.scale);119 }115 public EastNorth getEastNorth(int x, int y) { 116 return new EastNorth( 117 view_bounds.min.east() + x * this.scale, 118 view_bounds.min.north() - y * this.scale); 119 } 120 120 121 public boolean isVisible(int x, int y) {122 return true;123 }121 public boolean isVisible(int x, int y) { 122 return true; 123 } 124 124 125 public Graphics getGraphics() {126 return graphics;127 }125 public Graphics getGraphics() { 126 return graphics; 127 } 128 128 129 public void repaint() {130 }129 public void repaint() { 130 } 131 131 } -
applications/editors/josm/plugins/wms-turbo-challenge2/src/wmsturbochallenge/GameWindow.java
r19990 r23190 42 42 43 43 public class GameWindow extends JFrame implements ActionListener { 44 public GameWindow(Layer ground) {45 setTitle("The Ultimate WMS Super-speed Turbo Challenge II");46 setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);47 setUndecorated(true);48 setSize(s.getScreenSize().width, s.getScreenSize().height);49 setLocationRelativeTo(null);50 setResizable(false);51 52 while (s.getScreenSize().width < width * scale ||53 s.getScreenSize().height < height * scale)54 scale --;55 add(panel);56 57 setVisible(true);58 59 /* TODO: "Intro" screen perhaps with "Hall of Fame" */60 61 screen_image = new BufferedImage(width, height,62 BufferedImage.TYPE_INT_RGB);63 screen = screen_image.getGraphics();64 65 this.ground = ground;66 ground_view = new fake_map_view(Main.map.mapView, 0.0000001);67 68 /* Retrieve start position */69 EastNorth start = ground_view.parent.getCenter();70 lat = start.north();71 lon = start.east();72 73 addKeyListener(new TAdapter());74 75 timer = new Timer(80, this);76 timer.start();77 78 car_gps = new gps();79 car_gps.start();80 81 car_engine = new engine();82 car_engine.start();83 84 for (int i = 0; i < maxsprites; i ++)85 sprites[i] = new sprite_pos();86 87 generate_sky();88 }89 90 protected engine car_engine;91 92 protected gps car_gps;93 protected class gps extends Timer implements ActionListener {94 public gps() {95 super(1000, null);96 addActionListener(this);97 98 trackSegs = new ArrayList<Collection<WayPoint>>();99 }100 101 protected Collection<WayPoint> segment;102 protected Collection<Collection<WayPoint>> trackSegs;103 104 public void actionPerformed(ActionEvent e) {105 /* We should count the satellites here, see if we106 * have a fix and add any distortions. */107 108 segment.add(new WayPoint(Main.proj.eastNorth2latlon(109 new EastNorth(lon, lat))));110 }111 112 public void start() {113 super.start();114 115 /* Start recording */116 segment = new ArrayList<WayPoint>();117 trackSegs.add(segment);118 actionPerformed(null);119 }120 121 public void save_trace() {122 int len = 0;123 for (Collection<WayPoint> seg : trackSegs)124 len += seg.size();125 126 /* Don't save traces shorter than 5s */127 if (len <= 5)128 return;129 130 GpxData data = new GpxData();131 data.tracks.add(new ImmutableGpxTrack(trackSegs,132 new HashMap<String, Object>()));133 134 ground_view.parent.addLayer(135 new GpxLayer(data, "Car GPS trace"));136 }137 }138 139 /* These are EastNorth, not actual LatLon */140 protected double lat, lon;141 /* Camera's altitude above surface (same units as lat/lon above) */142 protected double ele = 0.000003;143 /* Cut off at ~75px from bottom of the screen */144 protected double horizon = 0.63;145 /* Car's distance from the camera lens */146 protected double cardist = ele * 3;147 148 /* Pixels per pixel, the bigger the more oldschool :-) */149 protected int scale = 5;150 151 protected BufferedImage screen_image;152 protected Graphics screen;153 protected int width = 320;154 protected int height = 200;155 protected int centre = width / 2;156 157 double maxdist = ele / (horizon - 0.6);158 double realwidth = maxdist * width / height;159 double pixelperlat = 1.0 * width / realwidth;160 double sratio = 0.85;161 protected int sw = (int) (2 * Math.PI * maxdist * pixelperlat * sratio);162 163 /* TODO: figure out how to load these dynamically after splash164 * screen is shown */165 protected static final ImageIcon car[] = new ImageIcon[] {166 new ImageIcon(Toolkit.getDefaultToolkit().createImage(167 WMSRacer.class.getResource(168 "/images/car0-l.png"))),169 new ImageIcon(Toolkit.getDefaultToolkit().createImage(170 WMSRacer.class.getResource(171 "/images/car0.png"))),172 new ImageIcon(Toolkit.getDefaultToolkit().createImage(173 WMSRacer.class.getResource(174 "/images/car0-r.png"))),175 new ImageIcon(Toolkit.getDefaultToolkit().createImage(176 WMSRacer.class.getResource(177 "/images/car1-l.png"))),178 new ImageIcon(Toolkit.getDefaultToolkit().createImage(179 WMSRacer.class.getResource(180 "/images/car1.png"))),181 new ImageIcon(Toolkit.getDefaultToolkit().createImage(182 WMSRacer.class.getResource(183 "/images/car1-r.png"))),184 };185 protected static final ImageIcon bg[] = new ImageIcon[] {186 new ImageIcon(Toolkit.getDefaultToolkit().createImage(187 WMSRacer.class.getResource(188 "/images/bg0.png"))),189 };190 protected static final ImageIcon skyline[] = new ImageIcon[] {191 new ImageIcon(Toolkit.getDefaultToolkit().createImage(192 WMSRacer.class.getResource(193 "/images/horizon.png"))),194 };195 protected static final ImageIcon cactus[] = new ImageIcon[] {196 new ImageIcon(Toolkit.getDefaultToolkit().createImage(197 WMSRacer.class.getResource(198 "/images/cactus0.png"))),199 new ImageIcon(Toolkit.getDefaultToolkit().createImage(200 WMSRacer.class.getResource(201 "/images/cactus1.png"))),202 new ImageIcon(Toolkit.getDefaultToolkit().createImage(203 WMSRacer.class.getResource(204 "/images/cactus2.png"))),205 };206 protected static final ImageIcon cloud[] = new ImageIcon[] {207 new ImageIcon(Toolkit.getDefaultToolkit().createImage(208 WMSRacer.class.getResource(209 "/images/cloud0.png"))),210 new ImageIcon(Toolkit.getDefaultToolkit().createImage(211 WMSRacer.class.getResource(212 "/images/cloud1.png"))),213 new ImageIcon(Toolkit.getDefaultToolkit().createImage(214 WMSRacer.class.getResource(215 "/images/cloud2.png"))),216 new ImageIcon(Toolkit.getDefaultToolkit().createImage(217 WMSRacer.class.getResource(218 "/images/cloud3.png"))),219 new ImageIcon(Toolkit.getDefaultToolkit().createImage(220 WMSRacer.class.getResource(221 "/images/cloud4.png"))),222 };223 protected static final ImageIcon aircraft[] = new ImageIcon[] {224 new ImageIcon(Toolkit.getDefaultToolkit().createImage(225 WMSRacer.class.getResource(226 "/images/aircraft0.png"))),227 };228 protected static final ImageIcon loading = new ImageIcon(229 Toolkit.getDefaultToolkit().createImage(230 WMSRacer.class.getResource(231 "/images/loading.png")));232 protected static Toolkit s = Toolkit.getDefaultToolkit();233 protected int current_bg = 0;234 protected int current_car = 0;235 protected boolean cacti_on = true;236 protected List<EastNorth> cacti = new ArrayList<EastNorth>();237 protected List<EastNorth> todelete = new ArrayList<EastNorth>();238 protected int splashframe = -1;239 protected EastNorth splashcactus;240 241 protected Layer ground;242 protected double heading = 0.0;243 protected double wheelangle = 0.0;244 protected double speed = 0.0;245 protected boolean key_down[] = new boolean[] {246 false, false, false, false, };247 248 protected void move() {249 /* Left */250 /* (At high speeds make more gentle turns) */251 if (key_down[0])252 wheelangle -= 0.1 / (1.0 + Math.abs(speed));253 /* Right */254 if (key_down[1])255 wheelangle += 0.1 / (1.0 + Math.abs(speed));256 if (wheelangle > 0.3)257 wheelangle = 0.3; /* Radians */258 if (wheelangle < -0.3)259 wheelangle = -0.3;260 261 wheelangle *= 0.7;262 263 /* Up */264 if (key_down[2])265 speed += speed >= 0.0 ? 1.0 / (2.0 + speed) : 0.5;266 /* Down */267 if (key_down[3]) {268 if (speed >= 0.5) /* Brake (TODO: sound) */269 speed -= 0.5;270 else if (speed >= 0.01) /* Brake (TODO: sound) */271 speed = 0.0;272 else /* Reverse */273 speed -= 0.5 / (4.0 - speed);274 }275 276 speed *= 0.97;277 car_engine.set_speed(speed);278 279 if (speed > -0.1 && speed < 0.1)280 speed = 0;281 282 heading += wheelangle * speed;283 284 boolean chop = false;285 double newlat = lat + Math.cos(heading) * speed * ele * 0.2;286 double newlon = lon + Math.sin(heading) * speed * ele * 0.2;287 for (EastNorth pos : cacti) {288 double alat = Math.abs(pos.north() - newlat);289 double alon = Math.abs(pos.east() - newlon);290 if (alat + alon < ele * 1.0) {291 if (Math.abs(speed) < 2.0) {292 if (speed > 0.0)293 speed = -0.5;294 else295 speed = 0.3;296 newlat = lat;297 newlon = lon;298 break;299 }300 301 chop = true;302 splashframe = 0;303 splashcactus = pos;304 todelete.add(pos);305 }306 }307 308 lat = newlat;309 lon = newlon;310 311 /* Seed a new cactus if we're moving.312 * TODO: hook into data layers and avoid putting the cactus on313 * the road!314 */315 if (cacti_on && Math.random() * 30.0 < speed) {316 double left_x = maxdist * (width - centre) / height;317 double right_x = maxdist * (0 - centre) / height;318 double x = left_x + Math.random() * (right_x - left_x);319 double clat = lat + (maxdist - cardist) *320 Math.cos(heading) - x * Math.sin(heading);321 double clon = lon + (maxdist - cardist) *322 Math.sin(heading) + x * Math.cos(heading);323 324 cacti.add(new EastNorth(clon, clat));325 chop = true;326 }327 328 /* Chop down any cactus far enough that it can't329 * be seen. ``If a cactus falls in a forest and330 * there is nobody around did it make a sound?''331 */332 if (chop) {333 for (EastNorth pos : cacti) {334 double alat = Math.abs(pos.north() - lat);335 double alon = Math.abs(pos.east() - lon);336 if (alat + alon > 2 * maxdist)337 todelete.add(pos);338 }339 cacti.removeAll(todelete);340 todelete = new ArrayList<EastNorth>();341 }342 }343 344 int frame;345 boolean downloading = false;346 protected void screen_repaint() {347 /* Draw background first */348 sky_paint();349 350 /* On top of it project the floor */351 ground_paint();352 353 /* Messages */354 frame ++;355 if ((frame & 8) == 0 && downloading)356 screen.drawImage(loading.getImage(), centre -357 loading.getIconWidth() / 2, 50, this);358 359 /* Sprites */360 sprites_paint();361 }362 363 static double max3(double x[]) {364 return x[0] > x[1] ? x[2] > x[0] ? x[2] : x[0] :365 (x[2] > x[1] ? x[2] : x[1]);366 }367 static double min3(double x[]) {368 return x[0] < x[1] ? x[2] < x[0] ? x[2] : x[0] :369 (x[2] < x[1] ? x[2] : x[1]);370 }371 372 protected void ground_paint() {373 double sin = Math.sin(heading);374 double cos = Math.cos(heading);375 376 /* First calculate the bounding box for the visible area.377 * The area will be (nearly) a triangle, so calculate the378 * EastNorth for the three corners and make a bounding box.379 */380 double left_x = maxdist * (width - centre) / height;381 double right_x = maxdist * (0 - centre) / height;382 double e_lat[] = new double[] {383 lat + (maxdist - cardist) * cos - left_x * sin,384 lat + (maxdist - cardist) * cos - right_x * sin,385 lat - cardist * cos, };386 double e_lon[] = new double[] {387 lon + (maxdist - cardist) * sin + left_x * cos,388 lon + (maxdist - cardist) * sin + right_x * cos,389 lon - cardist * sin, };390 ground_view.setProjectionBounds(new ProjectionBounds(391 new EastNorth(min3(e_lon), min3(e_lat)),392 new EastNorth(max3(e_lon), max3(e_lat))));393 394 /* If the layer is a WMS layer, check if any tiles are395 * missing */396 if (ground instanceof wmsplugin.WMSLayer) {397 wmsplugin.WMSLayer wms = (wmsplugin.WMSLayer) ground;398 downloading = wms.hasAutoDownload() && (399 null == wms.findImage(new EastNorth(400 e_lon[0], e_lat[0])) ||401 null == wms.findImage(new EastNorth(402 e_lon[0], e_lat[0])) ||403 null == wms.findImage(new EastNorth(404 e_lon[0], e_lat[0])));405 }406 407 /* Request the image from ground layer */408 ground.paint(ground_view.graphics, ground_view, null);409 410 for (int y = (int) (height * horizon + 0.1); y < height; y ++) {411 /* Assume a 60 deg vertical Field of View when412 * calculating the distance at given pixel. */413 double dist = ele / (1.0 * y / height - 0.6);414 double lat_off = lat + (dist - cardist) * cos;415 double lon_off = lon + (dist - cardist) * sin;416 417 for (int x = 0; x < width; x ++) {418 double p_x = dist * (x - centre) / height;419 420 EastNorth en = new EastNorth(421 lon_off + p_x * cos,422 lat_off - p_x * sin);423 424 Point pt = ground_view.getPoint(en);425 426 int rgb = ground_view.ground_image.getRGB(427 pt.x, pt.y);428 screen_image.setRGB(x, y, rgb);429 }430 }431 }432 433 protected BufferedImage sky_image;434 protected Graphics sky;435 public void generate_sky() {436 sky_image = new BufferedImage(sw, 70,437 BufferedImage.TYPE_INT_ARGB);438 sky = sky_image.getGraphics();439 440 int n = (int) (Math.random() * sw * 0.03);441 for (int i = 0; i < n; i ++) {442 int t = (int) (Math.random() * 5.0);443 int x = (int) (Math.random() *444 (sw - cloud[t].getIconWidth()));445 int y = (int) ((1 - Math.random() * Math.random()) *446 (70 - cloud[t].getIconHeight()));447 sky.drawImage(cloud[t].getImage(), x, y, this);448 }449 450 if (Math.random() < 0.5) {451 int t = 0;452 int x = (int) (300 + Math.random() * (sw - 500 -453 aircraft[t].getIconWidth()));454 sky.drawImage(aircraft[t].getImage(), x, 0, this);455 }456 }457 458 public void sky_paint() {459 /* for x -> 0, lim sin(x) / x = 1 */460 int hx = (int) (-heading * maxdist * pixelperlat);461 int hw = skyline[current_bg].getIconWidth();462 hx = ((hx % hw) - hw) % hw;463 464 int sx = (int) (-heading * maxdist * pixelperlat * sratio);465 sx = ((sx % sw) - sw) % sw;466 467 screen.drawImage(bg[current_bg].getImage(), 0, 0, this);468 screen.drawImage(sky_image, sx, 50, this);469 if (sw + sx < width)470 screen.drawImage(sky_image, sx + sw, 50, this);471 screen.drawImage(skyline[current_bg].getImage(), hx, 66, this);472 if (hw + hx < width)473 screen.drawImage(skyline[current_bg].getImage(),474 hx + hw, 66, this);475 }476 477 protected class sprite_pos implements Comparable {478 double dist;479 480 int x, y, sx, sy;481 Image sprite;482 483 public sprite_pos() {484 }485 486 public int compareTo(Object x) {487 sprite_pos other = (sprite_pos) x;488 return (int) ((other.dist - this.dist) * 1000000.0);489 }490 }491 492 /* sizes decides how many zoom levels the sprites have. We493 * could do just normal scalling according to distance but494 * that's not what old games did, they had prescaled sprites495 * for the different distances and you could see the feature496 * grow discretely as you approached it. */497 protected final static int sizes = 8;498 499 protected final static int maxsprites = 32;500 protected sprite_pos sprites[] = new sprite_pos[maxsprites];501 502 protected void sprites_paint() {503 /* The vehicle */504 int orientation = (wheelangle > -0.02 ? wheelangle < 0.02 ?505 1 : 2 : 0) + current_car * 3;506 sprites[0].sprite = car[orientation].getImage();507 sprites[0].dist = cardist;508 sprites[0].sx = car[orientation].getIconWidth();509 sprites[0].x = centre - sprites[0].sx / 2;510 sprites[0].sy = car[orientation].getIconHeight();511 sprites[0].y = height - sprites[0].sy - 10; /* TODO */512 513 /* The cacti */514 double sin = Math.sin(-heading);515 double cos = Math.cos(-heading);516 int i = 1;517 518 for (EastNorth ll : cacti) {519 double clat = ll.north() - lat;520 double clon = ll.east() - lon;521 double dist = (clat * cos - clon * sin) + cardist;522 double p_x = clat * sin + clon * cos;523 524 if (dist * 8 <= cardist || dist > maxdist)525 continue;526 527 int x = (int) (p_x * height / dist + centre);528 int y = (int) ((ele / dist + 0.6) * height);529 530 if (i >= maxsprites)531 break;532 if (x < -10 || x > width + 10)533 continue;534 535 int type = (((int) (ll.north() * 10000000.0) & 31) % 3);536 int sx = cactus[type].getIconWidth();537 int sy = cactus[type].getIconHeight();538 539 sprite_pos pos = sprites[i ++];540 pos.dist = dist;541 pos.sprite = cactus[type].getImage();542 pos.sx = (int) (sx * cardist * 0.7 / dist);543 pos.sy = (int) (sy * cardist * 0.7 / dist);544 pos.x = x - pos.sx / 2;545 pos.y = y - pos.sy;546 }547 548 Arrays.sort(sprites, 0, i);549 for (sprite_pos sprite : sprites)550 if (i --> 0)551 screen.drawImage(sprite.sprite,552 sprite.x, sprite.y,553 sprite.sx, sprite.sy, this);554 else555 break;556 557 if (splashframe >= 0) {558 splashframe ++;559 if (splashframe >= 8)560 splashframe = -1;561 562 int type = (((int) (splashcactus.north() *563 10000000.0) & 31) % 3);564 int sx = cactus[type].getIconWidth();565 int sy = cactus[type].getIconHeight();566 Image image = cactus[type].getImage();567 568 for (i = 0; i < 50; i ++) {569 int x = (int) (Math.random() * sx);570 int y = (int) (Math.random() * sy);571 int w = (int) (Math.random() * 20);572 int h = (int) (Math.random() * 20);573 int nx = centre + splashframe * (x - sx / 2);574 int ny = height - splashframe * (sy - y);575 int nw = w + splashframe;576 int nh = h + splashframe;577 578 screen.drawImage(image,579 nx, ny, nx + nw, ny + nh,580 x, y, x + w, y + h, this);581 }582 }583 }584 585 public boolean no_super_repaint = false;586 protected class GamePanel extends JPanel {587 public GamePanel() {588 setBackground(Color.BLACK);589 setDoubleBuffered(true);590 }591 592 public void paint(Graphics g) {593 int w = (int) getSize().getWidth();594 int h = (int) getSize().getHeight();595 596 if (no_super_repaint)597 no_super_repaint = false;598 else599 super.paint(g);600 601 g.drawImage(screen_image, (w - width * scale) / 2,602 (h - height * scale) / 2,603 width * scale, height * scale, this);604 605 Toolkit.getDefaultToolkit().sync();606 }607 }608 JPanel panel = new GamePanel();609 610 protected void quit() {611 timer.stop();612 613 car_engine.stop();614 615 car_gps.stop();616 car_gps.save_trace();617 618 setVisible(false);619 panel = null;620 screen_image = null;621 screen = null;622 dispose();623 }624 625 /*626 * Supposedly a thread drawing frames and sleeping in a loop is627 * better than for animating than swing Timers. For the moment628 * I'll use a timer because I don't want to deal with all the629 * potential threading issues.630 */631 protected Timer timer;632 public void actionPerformed(ActionEvent e) {633 move();634 screen_repaint();635 636 no_super_repaint = true;637 panel.repaint();638 }639 640 protected class TAdapter extends KeyAdapter {641 public void keyPressed(KeyEvent e) {642 int key = e.getKeyCode();643 644 if (key == KeyEvent.VK_LEFT && !key_down[0]) {645 wheelangle -= 0.02;646 key_down[0] = true;647 }648 649 if (key == KeyEvent.VK_RIGHT && !key_down[1]) {650 wheelangle += 0.02;651 key_down[1] = true;652 }653 654 if (key == KeyEvent.VK_UP)655 key_down[2] = true;656 657 if (key == KeyEvent.VK_DOWN)658 key_down[3] = true;659 660 if (key == KeyEvent.VK_ESCAPE)661 quit();662 663 /* Toggle sound */664 if (key == KeyEvent.VK_S) {665 if (car_engine.is_on())666 car_engine.stop();667 else668 car_engine.start();669 }670 671 /* Toggle cacti */672 if (key == KeyEvent.VK_C) {673 cacti_on = !cacti_on;674 if (!cacti_on)675 cacti = new ArrayList<EastNorth>();676 }677 678 /* Switch vehicle */679 if (key == KeyEvent.VK_V)680 if (current_car ++>= 1)681 current_car = 0;682 }683 684 public void keyReleased(KeyEvent e) {685 int key = e.getKeyCode();686 687 if (key == KeyEvent.VK_LEFT)688 key_down[0] = false;689 690 if (key == KeyEvent.VK_RIGHT)691 key_down[1] = false;692 693 if (key == KeyEvent.VK_UP)694 key_down[2] = false;695 696 if (key == KeyEvent.VK_DOWN)697 key_down[3] = false;698 }699 }700 protected fake_map_view ground_view;44 public GameWindow(Layer ground) { 45 setTitle("The Ultimate WMS Super-speed Turbo Challenge II"); 46 setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 47 setUndecorated(true); 48 setSize(s.getScreenSize().width, s.getScreenSize().height); 49 setLocationRelativeTo(null); 50 setResizable(false); 51 52 while (s.getScreenSize().width < width * scale || 53 s.getScreenSize().height < height * scale) 54 scale --; 55 add(panel); 56 57 setVisible(true); 58 59 /* TODO: "Intro" screen perhaps with "Hall of Fame" */ 60 61 screen_image = new BufferedImage(width, height, 62 BufferedImage.TYPE_INT_RGB); 63 screen = screen_image.getGraphics(); 64 65 this.ground = ground; 66 ground_view = new fake_map_view(Main.map.mapView, 0.0000001); 67 68 /* Retrieve start position */ 69 EastNorth start = ground_view.parent.getCenter(); 70 lat = start.north(); 71 lon = start.east(); 72 73 addKeyListener(new TAdapter()); 74 75 timer = new Timer(80, this); 76 timer.start(); 77 78 car_gps = new gps(); 79 car_gps.start(); 80 81 car_engine = new engine(); 82 car_engine.start(); 83 84 for (int i = 0; i < maxsprites; i ++) 85 sprites[i] = new sprite_pos(); 86 87 generate_sky(); 88 } 89 90 protected engine car_engine; 91 92 protected gps car_gps; 93 protected class gps extends Timer implements ActionListener { 94 public gps() { 95 super(1000, null); 96 addActionListener(this); 97 98 trackSegs = new ArrayList<Collection<WayPoint>>(); 99 } 100 101 protected Collection<WayPoint> segment; 102 protected Collection<Collection<WayPoint>> trackSegs; 103 104 public void actionPerformed(ActionEvent e) { 105 /* We should count the satellites here, see if we 106 * have a fix and add any distortions. */ 107 108 segment.add(new WayPoint(Main.proj.eastNorth2latlon( 109 new EastNorth(lon, lat)))); 110 } 111 112 public void start() { 113 super.start(); 114 115 /* Start recording */ 116 segment = new ArrayList<WayPoint>(); 117 trackSegs.add(segment); 118 actionPerformed(null); 119 } 120 121 public void save_trace() { 122 int len = 0; 123 for (Collection<WayPoint> seg : trackSegs) 124 len += seg.size(); 125 126 /* Don't save traces shorter than 5s */ 127 if (len <= 5) 128 return; 129 130 GpxData data = new GpxData(); 131 data.tracks.add(new ImmutableGpxTrack(trackSegs, 132 new HashMap<String, Object>())); 133 134 ground_view.parent.addLayer( 135 new GpxLayer(data, "Car GPS trace")); 136 } 137 } 138 139 /* These are EastNorth, not actual LatLon */ 140 protected double lat, lon; 141 /* Camera's altitude above surface (same units as lat/lon above) */ 142 protected double ele = 0.000003; 143 /* Cut off at ~75px from bottom of the screen */ 144 protected double horizon = 0.63; 145 /* Car's distance from the camera lens */ 146 protected double cardist = ele * 3; 147 148 /* Pixels per pixel, the bigger the more oldschool :-) */ 149 protected int scale = 5; 150 151 protected BufferedImage screen_image; 152 protected Graphics screen; 153 protected int width = 320; 154 protected int height = 200; 155 protected int centre = width / 2; 156 157 double maxdist = ele / (horizon - 0.6); 158 double realwidth = maxdist * width / height; 159 double pixelperlat = 1.0 * width / realwidth; 160 double sratio = 0.85; 161 protected int sw = (int) (2 * Math.PI * maxdist * pixelperlat * sratio); 162 163 /* TODO: figure out how to load these dynamically after splash 164 * screen is shown */ 165 protected static final ImageIcon car[] = new ImageIcon[] { 166 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 167 WMSRacer.class.getResource( 168 "/images/car0-l.png"))), 169 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 170 WMSRacer.class.getResource( 171 "/images/car0.png"))), 172 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 173 WMSRacer.class.getResource( 174 "/images/car0-r.png"))), 175 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 176 WMSRacer.class.getResource( 177 "/images/car1-l.png"))), 178 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 179 WMSRacer.class.getResource( 180 "/images/car1.png"))), 181 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 182 WMSRacer.class.getResource( 183 "/images/car1-r.png"))), 184 }; 185 protected static final ImageIcon bg[] = new ImageIcon[] { 186 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 187 WMSRacer.class.getResource( 188 "/images/bg0.png"))), 189 }; 190 protected static final ImageIcon skyline[] = new ImageIcon[] { 191 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 192 WMSRacer.class.getResource( 193 "/images/horizon.png"))), 194 }; 195 protected static final ImageIcon cactus[] = new ImageIcon[] { 196 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 197 WMSRacer.class.getResource( 198 "/images/cactus0.png"))), 199 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 200 WMSRacer.class.getResource( 201 "/images/cactus1.png"))), 202 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 203 WMSRacer.class.getResource( 204 "/images/cactus2.png"))), 205 }; 206 protected static final ImageIcon cloud[] = new ImageIcon[] { 207 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 208 WMSRacer.class.getResource( 209 "/images/cloud0.png"))), 210 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 211 WMSRacer.class.getResource( 212 "/images/cloud1.png"))), 213 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 214 WMSRacer.class.getResource( 215 "/images/cloud2.png"))), 216 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 217 WMSRacer.class.getResource( 218 "/images/cloud3.png"))), 219 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 220 WMSRacer.class.getResource( 221 "/images/cloud4.png"))), 222 }; 223 protected static final ImageIcon aircraft[] = new ImageIcon[] { 224 new ImageIcon(Toolkit.getDefaultToolkit().createImage( 225 WMSRacer.class.getResource( 226 "/images/aircraft0.png"))), 227 }; 228 protected static final ImageIcon loading = new ImageIcon( 229 Toolkit.getDefaultToolkit().createImage( 230 WMSRacer.class.getResource( 231 "/images/loading.png"))); 232 protected static Toolkit s = Toolkit.getDefaultToolkit(); 233 protected int current_bg = 0; 234 protected int current_car = 0; 235 protected boolean cacti_on = true; 236 protected List<EastNorth> cacti = new ArrayList<EastNorth>(); 237 protected List<EastNorth> todelete = new ArrayList<EastNorth>(); 238 protected int splashframe = -1; 239 protected EastNorth splashcactus; 240 241 protected Layer ground; 242 protected double heading = 0.0; 243 protected double wheelangle = 0.0; 244 protected double speed = 0.0; 245 protected boolean key_down[] = new boolean[] { 246 false, false, false, false, }; 247 248 protected void move() { 249 /* Left */ 250 /* (At high speeds make more gentle turns) */ 251 if (key_down[0]) 252 wheelangle -= 0.1 / (1.0 + Math.abs(speed)); 253 /* Right */ 254 if (key_down[1]) 255 wheelangle += 0.1 / (1.0 + Math.abs(speed)); 256 if (wheelangle > 0.3) 257 wheelangle = 0.3; /* Radians */ 258 if (wheelangle < -0.3) 259 wheelangle = -0.3; 260 261 wheelangle *= 0.7; 262 263 /* Up */ 264 if (key_down[2]) 265 speed += speed >= 0.0 ? 1.0 / (2.0 + speed) : 0.5; 266 /* Down */ 267 if (key_down[3]) { 268 if (speed >= 0.5) /* Brake (TODO: sound) */ 269 speed -= 0.5; 270 else if (speed >= 0.01) /* Brake (TODO: sound) */ 271 speed = 0.0; 272 else /* Reverse */ 273 speed -= 0.5 / (4.0 - speed); 274 } 275 276 speed *= 0.97; 277 car_engine.set_speed(speed); 278 279 if (speed > -0.1 && speed < 0.1) 280 speed = 0; 281 282 heading += wheelangle * speed; 283 284 boolean chop = false; 285 double newlat = lat + Math.cos(heading) * speed * ele * 0.2; 286 double newlon = lon + Math.sin(heading) * speed * ele * 0.2; 287 for (EastNorth pos : cacti) { 288 double alat = Math.abs(pos.north() - newlat); 289 double alon = Math.abs(pos.east() - newlon); 290 if (alat + alon < ele * 1.0) { 291 if (Math.abs(speed) < 2.0) { 292 if (speed > 0.0) 293 speed = -0.5; 294 else 295 speed = 0.3; 296 newlat = lat; 297 newlon = lon; 298 break; 299 } 300 301 chop = true; 302 splashframe = 0; 303 splashcactus = pos; 304 todelete.add(pos); 305 } 306 } 307 308 lat = newlat; 309 lon = newlon; 310 311 /* Seed a new cactus if we're moving. 312 * TODO: hook into data layers and avoid putting the cactus on 313 * the road! 314 */ 315 if (cacti_on && Math.random() * 30.0 < speed) { 316 double left_x = maxdist * (width - centre) / height; 317 double right_x = maxdist * (0 - centre) / height; 318 double x = left_x + Math.random() * (right_x - left_x); 319 double clat = lat + (maxdist - cardist) * 320 Math.cos(heading) - x * Math.sin(heading); 321 double clon = lon + (maxdist - cardist) * 322 Math.sin(heading) + x * Math.cos(heading); 323 324 cacti.add(new EastNorth(clon, clat)); 325 chop = true; 326 } 327 328 /* Chop down any cactus far enough that it can't 329 * be seen. ``If a cactus falls in a forest and 330 * there is nobody around did it make a sound?'' 331 */ 332 if (chop) { 333 for (EastNorth pos : cacti) { 334 double alat = Math.abs(pos.north() - lat); 335 double alon = Math.abs(pos.east() - lon); 336 if (alat + alon > 2 * maxdist) 337 todelete.add(pos); 338 } 339 cacti.removeAll(todelete); 340 todelete = new ArrayList<EastNorth>(); 341 } 342 } 343 344 int frame; 345 boolean downloading = false; 346 protected void screen_repaint() { 347 /* Draw background first */ 348 sky_paint(); 349 350 /* On top of it project the floor */ 351 ground_paint(); 352 353 /* Messages */ 354 frame ++; 355 if ((frame & 8) == 0 && downloading) 356 screen.drawImage(loading.getImage(), centre - 357 loading.getIconWidth() / 2, 50, this); 358 359 /* Sprites */ 360 sprites_paint(); 361 } 362 363 static double max3(double x[]) { 364 return x[0] > x[1] ? x[2] > x[0] ? x[2] : x[0] : 365 (x[2] > x[1] ? x[2] : x[1]); 366 } 367 static double min3(double x[]) { 368 return x[0] < x[1] ? x[2] < x[0] ? x[2] : x[0] : 369 (x[2] < x[1] ? x[2] : x[1]); 370 } 371 372 protected void ground_paint() { 373 double sin = Math.sin(heading); 374 double cos = Math.cos(heading); 375 376 /* First calculate the bounding box for the visible area. 377 * The area will be (nearly) a triangle, so calculate the 378 * EastNorth for the three corners and make a bounding box. 379 */ 380 double left_x = maxdist * (width - centre) / height; 381 double right_x = maxdist * (0 - centre) / height; 382 double e_lat[] = new double[] { 383 lat + (maxdist - cardist) * cos - left_x * sin, 384 lat + (maxdist - cardist) * cos - right_x * sin, 385 lat - cardist * cos, }; 386 double e_lon[] = new double[] { 387 lon + (maxdist - cardist) * sin + left_x * cos, 388 lon + (maxdist - cardist) * sin + right_x * cos, 389 lon - cardist * sin, }; 390 ground_view.setProjectionBounds(new ProjectionBounds( 391 new EastNorth(min3(e_lon), min3(e_lat)), 392 new EastNorth(max3(e_lon), max3(e_lat)))); 393 394 /* If the layer is a WMS layer, check if any tiles are 395 * missing */ 396 if (ground instanceof wmsplugin.WMSLayer) { 397 wmsplugin.WMSLayer wms = (wmsplugin.WMSLayer) ground; 398 downloading = wms.hasAutoDownload() && ( 399 null == wms.findImage(new EastNorth( 400 e_lon[0], e_lat[0])) || 401 null == wms.findImage(new EastNorth( 402 e_lon[0], e_lat[0])) || 403 null == wms.findImage(new EastNorth( 404 e_lon[0], e_lat[0]))); 405 } 406 407 /* Request the image from ground layer */ 408 ground.paint(ground_view.graphics, ground_view, null); 409 410 for (int y = (int) (height * horizon + 0.1); y < height; y ++) { 411 /* Assume a 60 deg vertical Field of View when 412 * calculating the distance at given pixel. */ 413 double dist = ele / (1.0 * y / height - 0.6); 414 double lat_off = lat + (dist - cardist) * cos; 415 double lon_off = lon + (dist - cardist) * sin; 416 417 for (int x = 0; x < width; x ++) { 418 double p_x = dist * (x - centre) / height; 419 420 EastNorth en = new EastNorth( 421 lon_off + p_x * cos, 422 lat_off - p_x * sin); 423 424 Point pt = ground_view.getPoint(en); 425 426 int rgb = ground_view.ground_image.getRGB( 427 pt.x, pt.y); 428 screen_image.setRGB(x, y, rgb); 429 } 430 } 431 } 432 433 protected BufferedImage sky_image; 434 protected Graphics sky; 435 public void generate_sky() { 436 sky_image = new BufferedImage(sw, 70, 437 BufferedImage.TYPE_INT_ARGB); 438 sky = sky_image.getGraphics(); 439 440 int n = (int) (Math.random() * sw * 0.03); 441 for (int i = 0; i < n; i ++) { 442 int t = (int) (Math.random() * 5.0); 443 int x = (int) (Math.random() * 444 (sw - cloud[t].getIconWidth())); 445 int y = (int) ((1 - Math.random() * Math.random()) * 446 (70 - cloud[t].getIconHeight())); 447 sky.drawImage(cloud[t].getImage(), x, y, this); 448 } 449 450 if (Math.random() < 0.5) { 451 int t = 0; 452 int x = (int) (300 + Math.random() * (sw - 500 - 453 aircraft[t].getIconWidth())); 454 sky.drawImage(aircraft[t].getImage(), x, 0, this); 455 } 456 } 457 458 public void sky_paint() { 459 /* for x -> 0, lim sin(x) / x = 1 */ 460 int hx = (int) (-heading * maxdist * pixelperlat); 461 int hw = skyline[current_bg].getIconWidth(); 462 hx = ((hx % hw) - hw) % hw; 463 464 int sx = (int) (-heading * maxdist * pixelperlat * sratio); 465 sx = ((sx % sw) - sw) % sw; 466 467 screen.drawImage(bg[current_bg].getImage(), 0, 0, this); 468 screen.drawImage(sky_image, sx, 50, this); 469 if (sw + sx < width) 470 screen.drawImage(sky_image, sx + sw, 50, this); 471 screen.drawImage(skyline[current_bg].getImage(), hx, 66, this); 472 if (hw + hx < width) 473 screen.drawImage(skyline[current_bg].getImage(), 474 hx + hw, 66, this); 475 } 476 477 protected class sprite_pos implements Comparable { 478 double dist; 479 480 int x, y, sx, sy; 481 Image sprite; 482 483 public sprite_pos() { 484 } 485 486 public int compareTo(Object x) { 487 sprite_pos other = (sprite_pos) x; 488 return (int) ((other.dist - this.dist) * 1000000.0); 489 } 490 } 491 492 /* sizes decides how many zoom levels the sprites have. We 493 * could do just normal scalling according to distance but 494 * that's not what old games did, they had prescaled sprites 495 * for the different distances and you could see the feature 496 * grow discretely as you approached it. */ 497 protected final static int sizes = 8; 498 499 protected final static int maxsprites = 32; 500 protected sprite_pos sprites[] = new sprite_pos[maxsprites]; 501 502 protected void sprites_paint() { 503 /* The vehicle */ 504 int orientation = (wheelangle > -0.02 ? wheelangle < 0.02 ? 505 1 : 2 : 0) + current_car * 3; 506 sprites[0].sprite = car[orientation].getImage(); 507 sprites[0].dist = cardist; 508 sprites[0].sx = car[orientation].getIconWidth(); 509 sprites[0].x = centre - sprites[0].sx / 2; 510 sprites[0].sy = car[orientation].getIconHeight(); 511 sprites[0].y = height - sprites[0].sy - 10; /* TODO */ 512 513 /* The cacti */ 514 double sin = Math.sin(-heading); 515 double cos = Math.cos(-heading); 516 int i = 1; 517 518 for (EastNorth ll : cacti) { 519 double clat = ll.north() - lat; 520 double clon = ll.east() - lon; 521 double dist = (clat * cos - clon * sin) + cardist; 522 double p_x = clat * sin + clon * cos; 523 524 if (dist * 8 <= cardist || dist > maxdist) 525 continue; 526 527 int x = (int) (p_x * height / dist + centre); 528 int y = (int) ((ele / dist + 0.6) * height); 529 530 if (i >= maxsprites) 531 break; 532 if (x < -10 || x > width + 10) 533 continue; 534 535 int type = (((int) (ll.north() * 10000000.0) & 31) % 3); 536 int sx = cactus[type].getIconWidth(); 537 int sy = cactus[type].getIconHeight(); 538 539 sprite_pos pos = sprites[i ++]; 540 pos.dist = dist; 541 pos.sprite = cactus[type].getImage(); 542 pos.sx = (int) (sx * cardist * 0.7 / dist); 543 pos.sy = (int) (sy * cardist * 0.7 / dist); 544 pos.x = x - pos.sx / 2; 545 pos.y = y - pos.sy; 546 } 547 548 Arrays.sort(sprites, 0, i); 549 for (sprite_pos sprite : sprites) 550 if (i --> 0) 551 screen.drawImage(sprite.sprite, 552 sprite.x, sprite.y, 553 sprite.sx, sprite.sy, this); 554 else 555 break; 556 557 if (splashframe >= 0) { 558 splashframe ++; 559 if (splashframe >= 8) 560 splashframe = -1; 561 562 int type = (((int) (splashcactus.north() * 563 10000000.0) & 31) % 3); 564 int sx = cactus[type].getIconWidth(); 565 int sy = cactus[type].getIconHeight(); 566 Image image = cactus[type].getImage(); 567 568 for (i = 0; i < 50; i ++) { 569 int x = (int) (Math.random() * sx); 570 int y = (int) (Math.random() * sy); 571 int w = (int) (Math.random() * 20); 572 int h = (int) (Math.random() * 20); 573 int nx = centre + splashframe * (x - sx / 2); 574 int ny = height - splashframe * (sy - y); 575 int nw = w + splashframe; 576 int nh = h + splashframe; 577 578 screen.drawImage(image, 579 nx, ny, nx + nw, ny + nh, 580 x, y, x + w, y + h, this); 581 } 582 } 583 } 584 585 public boolean no_super_repaint = false; 586 protected class GamePanel extends JPanel { 587 public GamePanel() { 588 setBackground(Color.BLACK); 589 setDoubleBuffered(true); 590 } 591 592 public void paint(Graphics g) { 593 int w = (int) getSize().getWidth(); 594 int h = (int) getSize().getHeight(); 595 596 if (no_super_repaint) 597 no_super_repaint = false; 598 else 599 super.paint(g); 600 601 g.drawImage(screen_image, (w - width * scale) / 2, 602 (h - height * scale) / 2, 603 width * scale, height * scale, this); 604 605 Toolkit.getDefaultToolkit().sync(); 606 } 607 } 608 JPanel panel = new GamePanel(); 609 610 protected void quit() { 611 timer.stop(); 612 613 car_engine.stop(); 614 615 car_gps.stop(); 616 car_gps.save_trace(); 617 618 setVisible(false); 619 panel = null; 620 screen_image = null; 621 screen = null; 622 dispose(); 623 } 624 625 /* 626 * Supposedly a thread drawing frames and sleeping in a loop is 627 * better than for animating than swing Timers. For the moment 628 * I'll use a timer because I don't want to deal with all the 629 * potential threading issues. 630 */ 631 protected Timer timer; 632 public void actionPerformed(ActionEvent e) { 633 move(); 634 screen_repaint(); 635 636 no_super_repaint = true; 637 panel.repaint(); 638 } 639 640 protected class TAdapter extends KeyAdapter { 641 public void keyPressed(KeyEvent e) { 642 int key = e.getKeyCode(); 643 644 if (key == KeyEvent.VK_LEFT && !key_down[0]) { 645 wheelangle -= 0.02; 646 key_down[0] = true; 647 } 648 649 if (key == KeyEvent.VK_RIGHT && !key_down[1]) { 650 wheelangle += 0.02; 651 key_down[1] = true; 652 } 653 654 if (key == KeyEvent.VK_UP) 655 key_down[2] = true; 656 657 if (key == KeyEvent.VK_DOWN) 658 key_down[3] = true; 659 660 if (key == KeyEvent.VK_ESCAPE) 661 quit(); 662 663 /* Toggle sound */ 664 if (key == KeyEvent.VK_S) { 665 if (car_engine.is_on()) 666 car_engine.stop(); 667 else 668 car_engine.start(); 669 } 670 671 /* Toggle cacti */ 672 if (key == KeyEvent.VK_C) { 673 cacti_on = !cacti_on; 674 if (!cacti_on) 675 cacti = new ArrayList<EastNorth>(); 676 } 677 678 /* Switch vehicle */ 679 if (key == KeyEvent.VK_V) 680 if (current_car ++>= 1) 681 current_car = 0; 682 } 683 684 public void keyReleased(KeyEvent e) { 685 int key = e.getKeyCode(); 686 687 if (key == KeyEvent.VK_LEFT) 688 key_down[0] = false; 689 690 if (key == KeyEvent.VK_RIGHT) 691 key_down[1] = false; 692 693 if (key == KeyEvent.VK_UP) 694 key_down[2] = false; 695 696 if (key == KeyEvent.VK_DOWN) 697 key_down[3] = false; 698 } 699 } 700 protected fake_map_view ground_view; 701 701 } -
applications/editors/josm/plugins/wms-turbo-challenge2/src/wmsturbochallenge/WMSRacer.java
r21761 r23190 20 20 21 21 public class WMSRacer extends Plugin implements LayerChangeListener { 22 public WMSRacer(PluginInformation info) {23 super(info);24 driveAction.updateEnabledState();22 public WMSRacer(PluginInformation info) { 23 super(info); 24 driveAction.updateEnabledState(); 25 25 26 JMenu toolsMenu = Main.main.menu.toolsMenu;27 toolsMenu.addSeparator();28 toolsMenu.add(new JMenuItem(driveAction));29 }26 JMenu toolsMenu = Main.main.menu.toolsMenu; 27 toolsMenu.addSeparator(); 28 toolsMenu.add(new JMenuItem(driveAction)); 29 } 30 30 31 /* Rather than add an action or main menu entry we should add32 * an entry in the new layer's context menus in layerAdded33 * but there doesn't seem to be any way to do that :( */34 protected class DriveAction extends JosmAction {35 public MapFrame frame = null;36 public Layer currentLayer = null;37 protected Layer groundLayer = null;31 /* Rather than add an action or main menu entry we should add 32 * an entry in the new layer's context menus in layerAdded 33 * but there doesn't seem to be any way to do that :( */ 34 protected class DriveAction extends JosmAction { 35 public MapFrame frame = null; 36 public Layer currentLayer = null; 37 protected Layer groundLayer = null; 38 38 39 public DriveAction() {40 super("Go driving", "wmsracer",41 "Drive a race car on this layer",42 null, true);43 setEnabled(false);44 }39 public DriveAction() { 40 super("Go driving", "wmsracer", 41 "Drive a race car on this layer", 42 null, true); 43 setEnabled(false); 44 } 45 45 46 public void actionPerformed(ActionEvent ev) {47 if (groundLayer == null ||48 !groundLayer.isBackgroundLayer())49 return;46 public void actionPerformed(ActionEvent ev) { 47 if (groundLayer == null || 48 !groundLayer.isBackgroundLayer()) 49 return; 50 50 51 new GameWindow(groundLayer);52 }51 new GameWindow(groundLayer); 52 } 53 53 54 public void updateEnabledState() {55 if (frame == null) {56 groundLayer = null;57 setEnabled(false);58 return;59 }54 public void updateEnabledState() { 55 if (frame == null) { 56 groundLayer = null; 57 setEnabled(false); 58 return; 59 } 60 60 61 if (currentLayer != null &&62 currentLayer.isBackgroundLayer()) {63 groundLayer = currentLayer;64 setEnabled(true);65 return;66 }61 if (currentLayer != null && 62 currentLayer.isBackgroundLayer()) { 63 groundLayer = currentLayer; 64 setEnabled(true); 65 return; 66 } 67 67 68 /* TODO: should only iterate through visible layers?69 * or only wms layers? or perhaps we should allow70 * driving on data/gpx layers too, or the full layer71 * stack (by calling mapView.paint() instead of72 * layer.paint()? Nah.73 * (Note that for GPX or Data layers we could do74 * some clever rendering directly on our perspectivic75 * pseudo-3d surface by defining a strange projection76 * like that or rendering in "stripes" at different77 * horizontal scanlines (lines equidistant from78 * camera eye)) */79 for (Layer l : frame.mapView.getAllLayers())80 if (l.isBackgroundLayer()) {81 groundLayer = l;82 setEnabled(true);83 return;84 }68 /* TODO: should only iterate through visible layers? 69 * or only wms layers? or perhaps we should allow 70 * driving on data/gpx layers too, or the full layer 71 * stack (by calling mapView.paint() instead of 72 * layer.paint()? Nah. 73 * (Note that for GPX or Data layers we could do 74 * some clever rendering directly on our perspectivic 75 * pseudo-3d surface by defining a strange projection 76 * like that or rendering in "stripes" at different 77 * horizontal scanlines (lines equidistant from 78 * camera eye)) */ 79 for (Layer l : frame.mapView.getAllLayers()) 80 if (l.isBackgroundLayer()) { 81 groundLayer = l; 82 setEnabled(true); 83 return; 84 } 85 85 86 groundLayer = null;87 setEnabled(false);88 }89 }86 groundLayer = null; 87 setEnabled(false); 88 } 89 } 90 90 91 protected DriveAction driveAction = new DriveAction();91 protected DriveAction driveAction = new DriveAction(); 92 92 93 public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {94 if (oldFrame != null)95 oldFrame.mapView.removeLayerChangeListener(this);93 public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) { 94 if (oldFrame != null) 95 oldFrame.mapView.removeLayerChangeListener(this); 96 96 97 driveAction.frame = newFrame;98 driveAction.updateEnabledState();97 driveAction.frame = newFrame; 98 driveAction.updateEnabledState(); 99 99 100 if (newFrame != null)101 newFrame.mapView.addLayerChangeListener(this);102 }100 if (newFrame != null) 101 newFrame.mapView.addLayerChangeListener(this); 102 } 103 103 104 /* LayerChangeListener methods */105 public void activeLayerChange(Layer oldLayer, Layer newLayer) {106 driveAction.currentLayer = newLayer;107 driveAction.updateEnabledState();108 }104 /* LayerChangeListener methods */ 105 public void activeLayerChange(Layer oldLayer, Layer newLayer) { 106 driveAction.currentLayer = newLayer; 107 driveAction.updateEnabledState(); 108 } 109 109 110 public void layerAdded(Layer newLayer) {111 driveAction.updateEnabledState();112 }110 public void layerAdded(Layer newLayer) { 111 driveAction.updateEnabledState(); 112 } 113 113 114 public void layerRemoved(Layer oldLayer) {115 driveAction.updateEnabledState();116 }114 public void layerRemoved(Layer oldLayer) { 115 driveAction.updateEnabledState(); 116 } 117 117 }
Note:
See TracChangeset
for help on using the changeset viewer.
