OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Native Client Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "nacl_app/flock.h" | |
6 | |
7 #include <algorithm> | |
8 #include <cmath> | |
9 #include <cstring> | |
10 #include <string> | |
11 | |
12 #include "nacl_app/scoped_pixel_lock.h" | |
13 #include "ppapi/cpp/size.h" | |
14 #include "threading/scoped_mutex_lock.h" | |
15 | |
16 namespace { | |
17 const uint32_t kBackgroundColor = 0xFFFFFFFF; // Opaque white. | |
18 | |
19 // Throttle the simulation to run a fixed number of ticks per Render() call. | |
20 const int32_t kSimulationTicksPerRender = 16; | |
21 | |
22 // The goose sprites rotate in increments of 5 degrees. | |
23 const double kGooseHeadingIncrement = (5.0 * M_PI) / 180.0; | |
24 } // namespace | |
25 | |
26 namespace flocking_geese { | |
27 | |
28 Flock::Flock() : flock_simulation_thread_(NULL), | |
29 sim_state_condition_(kSimulationStopped), | |
30 simulation_mode_(kPaused), | |
31 tick_counter_(0) { | |
32 pthread_mutex_init(&flock_simulation_mutex_, NULL); | |
33 } | |
34 | |
35 Flock::~Flock() { | |
36 set_is_simulation_running(false); | |
37 if (flock_simulation_thread_) { | |
38 pthread_join(flock_simulation_thread_, NULL); | |
39 } | |
40 pthread_mutex_destroy(&flock_simulation_mutex_); | |
41 } | |
42 | |
43 void Flock::StartSimulation() { | |
44 pthread_create(&flock_simulation_thread_, NULL, FlockSimulation, this); | |
45 } | |
46 | |
47 void Flock::set_simulation_mode(Flock::SimulationMode new_mode) { | |
48 simulation_mode_.Lock(); | |
49 if (new_mode == kRunning) | |
50 set_tick_counter(0); | |
51 simulation_mode_.UnlockWithCondition(new_mode); | |
52 } | |
53 | |
54 Flock::SimulationMode Flock::WaitForRunMode() { | |
55 simulation_mode_.LockWhenCondition(kRunning); | |
56 simulation_mode_.Unlock(); | |
57 return simulation_mode(); | |
58 } | |
59 | |
60 void Flock::SetAttractorAtIndex(const Vector2& attractor, size_t index) { | |
61 if (index >= attractors_.size()) { | |
62 attractors_.resize(index + 1, Vector2()); | |
63 } | |
64 attractors_[index] = attractor; | |
65 } | |
66 | |
67 void Flock::Resize(const pp::Size& size) { | |
68 flockBounds_.SetRect(0, 0, | |
69 std::max(size.width(), 0), | |
70 std::max(size.height(), 0)); | |
71 } | |
72 | |
73 void Flock::ResetFlock(size_t size, const Vector2& initialLocation) { | |
74 threading::ScopedMutexLock scoped_mutex(simulation_mutex()); | |
75 if (!scoped_mutex.is_valid()) | |
76 return; | |
77 geese_.resize(size + 1); | |
78 geese_.assign(size, Goose(initialLocation)); | |
79 frame_counter_.Reset(); | |
80 } | |
81 | |
82 void Flock::SimulationTick() { | |
83 frame_counter_.BeginFrame(); | |
84 for (std::vector<Goose>::iterator goose = geese_.begin(); | |
85 goose < geese_.end(); | |
86 ++goose) { | |
87 goose->SimulationTick(geese_, attractors_, flockBounds_); | |
88 } | |
89 frame_counter_.EndFrame(); | |
90 } | |
91 | |
92 void Flock::Render() { | |
93 ScopedPixelLock scoped_pixel_lock(shared_pixel_buffer_); | |
94 uint32_t* pixel_buffer = scoped_pixel_lock.pixels(); | |
95 if (pixel_buffer == NULL || goose_sprite_ == NULL) { | |
96 // Note that if the pixel buffer never gets initialized, this won't ever | |
97 // paint anything. Which is probably the right thing to do. Also, this | |
98 // clause means that the image will not get the very first few sim cells, | |
99 // since it's possible that this thread starts before the pixel buffer is | |
100 // initialized. | |
101 return; | |
102 } | |
103 // Clear out the pixel buffer, then render all the geese. | |
104 pp::Rect canvas_bounds(pp::Point(), shared_pixel_buffer_->size()); | |
105 int32_t canvas_width = canvas_bounds.size().width(); | |
106 int32_t canvas_height = canvas_bounds.size().height(); | |
107 const size_t pixel_count = canvas_width * canvas_height; | |
108 std::fill(pixel_buffer, pixel_buffer + pixel_count, kBackgroundColor); | |
109 threading::ScopedMutexLock scoped_mutex(simulation_mutex()); | |
110 if (!scoped_mutex.is_valid()) | |
111 return; | |
112 | |
113 int32_t sprite_stamp_width = goose_sprite_->size().height(); | |
114 pp::Rect sprite_src_rect(0, 0, | |
115 sprite_stamp_width, goose_sprite_->size().height()); | |
116 for (std::vector<Goose>::const_iterator goose = geese_.begin(); | |
117 goose < geese_.end(); | |
118 ++goose) { | |
119 pp::Point dest_point(goose->location().x() - sprite_stamp_width / 2, | |
120 goose->location().y() - sprite_stamp_width / 2); | |
121 double heading = goose->velocity().Heading(); | |
122 if (heading < 0.0) | |
123 heading = M_PI * 2.0 + heading; | |
124 int32_t sprite_index = | |
125 static_cast<int32_t>(heading / kGooseHeadingIncrement) * | |
126 sprite_stamp_width; | |
127 sprite_src_rect.set_x(sprite_index); | |
128 goose_sprite_->CompositeFromRectToPoint( | |
129 sprite_src_rect, | |
130 pixel_buffer, canvas_bounds, 0, | |
131 dest_point); | |
132 } | |
133 | |
134 // Hack: draw a blue rectangle at each attractor. | |
135 // TODO(dspringer): This is ridiculous. Fix this when a proper graphics lib | |
136 // available. | |
137 for (std::vector<Vector2>::const_iterator attractor = attractors_.begin(); | |
138 attractor < attractors_.end(); | |
139 ++attractor) { | |
140 int32_t x = std::max(static_cast<int32_t>(attractor->x()) - 2, 0); | |
141 int32_t y = std::max(static_cast<int32_t>(attractor->y()) - 2, 0); | |
142 int32_t w = 4, h = 4; | |
143 int32_t rw = canvas_width; | |
144 if (x + w >= rw) { x = rw - 1 - w; } | |
145 if (y + h >= canvas_height) { y = canvas_height - 1 - h; } | |
146 uint32_t* rect = pixel_buffer + y * rw + x; | |
147 for (int32_t row = 0; row < h; ++row) { | |
148 uint32_t* scanline = rect; | |
149 rect += rw; | |
150 *scanline++ = 0xFF0000FF; | |
151 *scanline++ = 0xFF0000FF; | |
152 *scanline++ = 0xFF0000FF; | |
153 *scanline++ = 0xFF0000FF; | |
154 } | |
155 } | |
156 | |
157 if (simulation_mode() == kRenderThrottle) { | |
158 set_simulation_mode(kRunning); | |
159 } | |
160 } | |
161 | |
162 void* Flock::FlockSimulation(void* param) { | |
163 Flock* flock = static_cast<Flock*>(param); | |
164 // Run the Life simulation in an endless loop. Shut this down when | |
165 // is_simulation_running() returns |false|. | |
166 flock->set_is_simulation_running(true); | |
167 flock->set_tick_counter(0); | |
168 while (flock->is_simulation_running()) { | |
169 flock->WaitForRunMode(); | |
170 flock->SimulationTick(); | |
171 // Throttle the simulation so it doesn't outrun the browser by too much. | |
172 if (flock->increment_tick_counter() > kSimulationTicksPerRender) { | |
173 flock->set_simulation_mode(kRenderThrottle); | |
174 } | |
175 //flock->Render(); | |
176 } | |
177 return NULL; | |
178 } | |
179 } // namespace flocking_geese | |
180 | |
OLD | NEW |