1 #include "FleetButton.h"
2 
3 #include "FleetWnd.h"
4 #include "MapWnd.h"
5 #include "Sound.h"
6 #include "CUIDrawUtil.h"
7 #include "CUIControls.h"
8 #include "../util/i18n.h"
9 #include "../util/Logger.h"
10 #include "../util/OptionsDB.h"
11 #include "../client/human/HumanClientApp.h"
12 #include "../universe/Fleet.h"
13 #include "../universe/Ship.h"
14 #include "../universe/System.h"
15 #include "../universe/Enums.h"
16 #include "../Empire/Empire.h"
17 
18 #include <GG/GLClientAndServerBuffer.h>
19 
20 #include <algorithm>
21 
22 
23 namespace {
24     /* returns number of fleet icon size texture to use to represent fleet(s) with the passed number of ships */
FleetSizeIconNumber(int number_ships)25     int FleetSizeIconNumber(int number_ships) {
26         // one ship (or zero?) has no marker.  more marker levels are used for each doubling in the number of ships
27         number_ships = std::min(std::max(number_ships, 1), 129);    // smallest size indicator is for 1 ship, largest is for 128 or greater
28         if (number_ships < 2)
29             return 0;
30         else if (number_ships < 4)
31             return 1;
32         else if (number_ships < 8)
33             return 2;
34         else if (number_ships < 16)
35             return 3;
36         else if (number_ships < 32)
37             return 4;
38         else if (number_ships < 64)
39             return 5;
40         else if (number_ships < 128)
41             return 6;
42         else  //(number_ships >= 128)
43             return 7;
44     }
45 
46     /* returns prefix of filename used for icons for the indicated fleet button size type */
FleetIconSizePrefix(FleetButton::SizeType size_type)47     std::string FleetIconSizePrefix(FleetButton::SizeType size_type) {
48         if (size_type == FleetButton::SizeType::LARGE)
49             return "big-";
50         else if (size_type == FleetButton::SizeType::MEDIUM)
51             return "med-";
52         else if (size_type == FleetButton::SizeType::SMALL)
53             return "sml-";
54         else
55             return "";
56     }
57 
AddOptions(OptionsDB & db)58     void AddOptions(OptionsDB& db) {
59         db.Add("ui.map.fleet.select.indicator.size", UserStringNop("OPTIONS_DB_UI_FLEET_SELECTION_INDICATOR_SIZE"), 1.625, RangedStepValidator<double>(0.125, 0.5, 5));
60     }
61     bool temp_bool = RegisterOptions(&AddOptions);
62 
63     const float TWO_PI = 2.0*3.14159f;
64 }
65 
66 ///////////////////////////
67 // FleetButton           //
68 ///////////////////////////
FleetButton(int fleet_id,SizeType size_type)69 FleetButton::FleetButton(int fleet_id, SizeType size_type) :
70     FleetButton(std::vector<int>(1, fleet_id), size_type)
71 {}
72 
FleetButton(const std::vector<int> & fleet_IDs,SizeType size_type)73 FleetButton::FleetButton(const std::vector<int>& fleet_IDs, SizeType size_type) :
74     GG::Button("", nullptr, GG::CLR_ZERO)
75 {
76     std::vector<std::shared_ptr<const Fleet>> fleets;
77     fleets.reserve(fleet_IDs.size());
78     for (const auto& fleet : Objects().find<Fleet>(fleet_IDs)) {
79         if (!fleet)
80             continue;
81         m_fleets.push_back(fleet->ID());
82         fleets.push_back(fleet);
83     }
84 
85     // determine owner(s) of fleet(s).  Only care whether or not there is more than one owner, as owner
86     // is used to determine colouration
87     int owner_id = ALL_EMPIRES;
88     int multiple_owners = false;
89     if (fleets.empty()) {
90         // leave as ALL_EMPIRES
91     } else if (fleets.size() == 1) {
92         owner_id = (*fleets.begin())->Owner();
93     } else {
94         owner_id = (*fleets.begin())->Owner();
95         // use ALL_EMPIRES if there are multiple owners (including no owner and an owner)
96         for (auto& fleet : fleets) {
97             if (fleet->Owner() != owner_id) {
98                 owner_id = ALL_EMPIRES;
99                 multiple_owners = true;
100                 break;
101             }
102         }
103     }
104 
105 
106     // get fleet colour
107     if (multiple_owners) {
108         SetColor(GG::CLR_WHITE);
109     } else if (owner_id == ALL_EMPIRES) {
110         // all ships owned by now empire
111         bool monsters = true;
112         // find if any ship in fleets in button is not a monster
113         for (auto& fleet : fleets) {
114             for (const auto& ship : Objects().find<Ship>(fleet->ShipIDs())) {
115                 if (!ship)
116                     continue;
117                 if (!ship->IsMonster()) {
118                     monsters = false;
119                     break;
120                 }
121             }
122         }
123 
124         if (monsters)
125             SetColor(GG::CLR_RED);
126         else
127             SetColor(GG::CLR_WHITE);
128     } else {
129         // single empire owner
130         if (const Empire* empire = GetEmpire(owner_id))
131             SetColor(empire->Color());
132         else
133             SetColor(GG::CLR_GRAY); // should never be necessary... but just in case
134     }
135 
136 
137     // determine direction button should be rotated to orient along a starlane
138     GLfloat pointing_angle = 0.0f;
139 
140     std::shared_ptr<const Fleet> first_fleet;
141     if (!m_fleets.empty())
142         first_fleet = *fleets.begin();
143     if (first_fleet && first_fleet->SystemID() == INVALID_OBJECT_ID && first_fleet->NextSystemID() != INVALID_OBJECT_ID) {
144         int next_sys_id = first_fleet->NextSystemID();
145         if (auto obj = Objects().get(next_sys_id)) {
146             // fleet is not in a system and has a valid next destination, so can orient it in that direction
147             // fleet icons might not appear on the screen in the exact place corresponding to their
148             // actual universe position, but if they're moving along a starlane, this code will assume
149             // their apparent position will only be different from their true position in a direction
150             // parallel with the starlane, so the direction from their true position to their destination
151             // position can be used to get a direction vector to orient the icon
152             float dest_x = obj->X(), dest_y = obj->Y();
153             float cur_x = first_fleet->X(), cur_y = first_fleet->Y();
154             const auto& map_wnd = ClientUI::GetClientUI()->GetMapWnd();
155             GG::Pt dest = map_wnd->ScreenCoordsFromUniversePosition(dest_x, dest_y);
156             GG::Pt cur = map_wnd->ScreenCoordsFromUniversePosition(cur_x, cur_y);
157             GG::Pt direction_vector = dest - cur;
158 
159             if (direction_vector.x != GG::X0 || direction_vector.y != GG::Y0)
160                 pointing_angle = 360.0f / TWO_PI * std::atan2(static_cast<float>(Value(direction_vector.y)), static_cast<float>(Value(direction_vector.x))) + 90;
161         }
162     }
163 
164     // select icon(s) for fleet(s)
165     int num_ships = 0;
166     m_fleet_blockaded = false;
167     for (auto& fleet : fleets) {
168         if (fleet) {
169             num_ships += fleet->NumShips();
170             if (!m_fleet_blockaded && fleet->Blockaded())
171                 m_fleet_blockaded = true;
172         }
173     }
174 
175     // add graphics for all icons needed
176     m_icons.reserve(4);
177 
178     if (m_fleet_blockaded) {
179         if (auto blockaded_texture = FleetBlockadedIcon(size_type)) {
180             auto icon = GG::Wnd::Create<GG::StaticGraphic>(blockaded_texture, GG::GRAPHIC_FITGRAPHIC | GG::GRAPHIC_PROPSCALE);
181             GG::Clr opposite_clr(255 - this->Color().r, 255 - this->Color().g, 255 - this->Color().b, this->Color().a);
182             icon->SetColor(opposite_clr);
183             m_icons.push_back(icon);
184         }
185     }
186 
187     if (auto size_texture = FleetSizeIcon(num_ships, size_type)) {
188         auto icon = GG::Wnd::Create<RotatingGraphic>(size_texture, GG::GRAPHIC_FITGRAPHIC | GG::GRAPHIC_PROPSCALE);
189         icon->SetPhaseOffset(pointing_angle);
190         icon->SetRPM(0.0f);
191         icon->SetColor(this->Color());
192         m_icons.push_back(icon);
193         Resize(GG::Pt(size_texture->DefaultWidth(), size_texture->DefaultHeight()));
194     }
195 
196     for (auto& texture : FleetHeadIcons(fleets, size_type)) {
197         auto icon = GG::Wnd::Create<RotatingGraphic>(texture, GG::GRAPHIC_FITGRAPHIC | GG::GRAPHIC_PROPSCALE);
198         icon->SetPhaseOffset(pointing_angle);
199         icon->SetRPM(0.0f);
200         icon->SetColor(this->Color());
201         m_icons.push_back(icon);
202         if (Width() < texture->DefaultWidth())
203             Resize(GG::Pt(texture->DefaultWidth(), texture->DefaultHeight()));
204     }
205 
206     // set up selection indicator
207     m_selection_indicator = GG::Wnd::Create<RotatingGraphic>(FleetSelectionIndicatorIcon(), GG::GRAPHIC_FITGRAPHIC | GG::GRAPHIC_PROPSCALE);
208     m_selection_indicator->SetRPM(ClientUI::SystemSelectionIndicatorRPM());
209 
210     LayoutIcons();
211 
212     // Scanlines for not currently-visible objects?
213     int empire_id = HumanClientApp::GetApp()->EmpireID();
214     if (empire_id == ALL_EMPIRES || !GetOptionsDB().Get<bool>("ui.map.scanlines.shown"))
215         return;
216 
217     // Create scanline renderer control, use opposite color of fleet btn
218     GG::Clr opposite_clr(255 - Color().r, 255 - Color().g, 255 - Color().b, 64);
219     m_scanline_control = GG::Wnd::Create<ScanlineControl>(GG::X0, GG::Y0, Width(), Height(), false, opposite_clr);
220 }
221 
CompleteConstruction()222 void FleetButton::CompleteConstruction() {
223     Button::CompleteConstruction();
224 
225     for (auto& icon: m_icons)
226         AttachChild(icon);
227 
228     // Scanlines for not currently-visible objects?
229     int empire_id = HumanClientApp::GetApp()->EmpireID();
230     if (empire_id == ALL_EMPIRES || !GetOptionsDB().Get<bool>("ui.map.scanlines.shown"))
231         return;
232 
233     bool at_least_one_fleet_visible = false;
234     for (int fleet_id : m_fleets) {
235         if (GetUniverse().GetObjectVisibilityByEmpire(fleet_id, empire_id) >= VIS_BASIC_VISIBILITY) {
236             at_least_one_fleet_visible = true;
237             break;
238         }
239     }
240 
241     if (!at_least_one_fleet_visible)
242         AttachChild(m_scanline_control);
243 
244     SetBrowseModeTime(GetOptionsDB().Get<int>("ui.tooltip.delay"));
245 }
246 
~FleetButton()247 FleetButton::~FleetButton()
248 {}
249 
InWindow(const GG::Pt & pt) const250 bool FleetButton::InWindow(const GG::Pt& pt) const {
251     // find if cursor is within required distance of centre of icon
252     GG::Pt ul = UpperLeft(), lr = LowerRight();
253     const float midX = Value(ul.x + lr.x)/2.0f;
254     const float midY = Value(ul.y + lr.y)/2.0f;
255 
256     const float RADIUS2 = Value(Width())*Value(Width())/4.0f;
257 
258     const float ptX = Value(pt.x);
259     const float ptY = Value(pt.y);
260 
261     const float distx = ptX - midX, disty = ptY - midY;
262 
263     return distx*distx + disty*disty <= RADIUS2;
264 }
265 
MouseHere(const GG::Pt & pt,GG::Flags<GG::ModKey> mod_keys)266 void FleetButton::MouseHere(const GG::Pt& pt, GG::Flags<GG::ModKey> mod_keys) {
267     const auto& map_wnd = ClientUI::GetClientUI()->GetMapWnd();
268     if (!Disabled() && (!map_wnd || !map_wnd->InProductionViewMode())) {
269         if (State() != BN_ROLLOVER)
270             PlayFleetButtonRolloverSound();
271         SetState(BN_ROLLOVER);
272     }
273 }
274 
SizeMove(const GG::Pt & ul,const GG::Pt & lr)275 void FleetButton::SizeMove(const GG::Pt& ul, const GG::Pt& lr) {
276     GG::Pt sz = Size();
277 
278     Button::SizeMove(ul, lr);
279 
280     if (sz == Size())
281         return;
282 
283     LayoutIcons();
284 }
285 
LayoutIcons()286 void FleetButton::LayoutIcons() {
287     GG::Pt middle = GG::Pt(Width() / 2, Height() / 2);
288     for (auto& graphic : m_icons) {
289         GG::SubTexture subtexture = graphic->GetTexture();
290         GG::Pt subtexture_sz = GG::Pt(subtexture.Width(), subtexture.Height());
291         GG::Pt graphic_ul = middle - GG::Pt(subtexture_sz.x / 2, subtexture_sz.y / 2);
292         graphic->SizeMove(graphic_ul, graphic_ul + subtexture_sz);
293     }
294 
295     if (m_selection_indicator) {
296         //GG::SubTexture subtexture = m_selection_indicator->GetTexture();
297         //GG::Pt subtexture_sz = GG::Pt(subtexture.Width(), subtexture.Height());
298         double sel_ind_scale = GetOptionsDB().Get<double>("ui.map.fleet.select.indicator.size");
299         GG::Pt subtexture_sz = Size() * sel_ind_scale;
300         GG::Pt graphic_ul = middle - subtexture_sz / 2;
301         m_selection_indicator->SizeMove(graphic_ul, graphic_ul + subtexture_sz);
302     }
303 
304     // refresh fleet button tooltip
305     if (m_fleet_blockaded) {
306         std::shared_ptr<Fleet> fleet;
307         std::string available_exits = "";
308         int available_exits_count = 0;
309 
310         if (!m_fleets.empty())
311             // can just pick first fleet because all fleets in system should have same exits
312             fleet = Objects().get<Fleet>(*m_fleets.begin());
313         else return;
314 
315         for (const auto& target_system_id : Objects().get<System>(fleet->SystemID())->StarlanesWormholes()) {
316             if (fleet->BlockadedAtSystem(fleet->SystemID(), target_system_id.first))
317                 continue;
318 
319             auto target_system = Objects().get<System>(target_system_id.first);
320             if (target_system) {
321                 available_exits += "\n" + target_system->ApparentName(HumanClientApp::GetApp()->EmpireID());
322                 available_exits_count++;
323             }
324         }
325 
326         if (fleet->Owner() == ALL_EMPIRES)  // as above, if first fleet of fleet-button is "monster-owned", all fleets are
327             SetBrowseText(UserString("FB_TOOLTIP_BLOCKADE_MONSTER"));
328         else if (available_exits_count >= 1)
329             SetBrowseText(UserString("FB_TOOLTIP_BLOCKADE_WITH_EXIT") + available_exits);
330         else
331             SetBrowseText(UserString("FB_TOOLTIP_BLOCKADE_NO_EXIT"));
332     } else {
333         ClearBrowseInfoWnd();
334     }
335 }
336 
SetSelected(bool selected)337 void FleetButton::SetSelected(bool selected) {
338     m_selected = selected;
339 
340     if (!m_selected) {
341         DetachChild(m_selection_indicator);
342         m_selection_indicator->Hide();
343         return;
344     }
345 
346     AttachChild(m_selection_indicator);
347     m_selection_indicator->Show();
348     MoveChildDown(m_selection_indicator);
349     LayoutIcons();
350 }
351 
RenderUnpressed()352 void FleetButton::RenderUnpressed() {
353     // GG::Pt ul = UpperLeft(), lr = LowerRight();
354     // const float midX = Value(ul.x + lr.x)/2.0f;
355     // const float midY = Value(ul.y + lr.y)/2.0f;
356 
357     //// debug
358     //GG::FlatRectangle(ul, lr, GG::CLR_ZERO, GG::CLR_RED, 2);
359     //// end debug
360 }
361 
RenderPressed()362 void FleetButton::RenderPressed() {
363     glDisable(GL_TEXTURE_2D);
364     glColor(Color());
365     CircleArc(UpperLeft(), LowerRight(), 0.0, TWO_PI, true);
366     glEnable(GL_TEXTURE_2D);
367 
368     RenderUnpressed();
369 }
370 
RenderRollover()371 void FleetButton::RenderRollover() {
372     glDisable(GL_TEXTURE_2D);
373     glColor(GG::CLR_WHITE);
374     CircleArc(UpperLeft(), LowerRight(), 0.0, TWO_PI, true);
375     glEnable(GL_TEXTURE_2D);
376 
377     RenderUnpressed();
378 }
379 
PlayFleetButtonRolloverSound()380 void FleetButton::PlayFleetButtonRolloverSound()
381 { Sound::GetSound().PlaySound(GetOptionsDB().Get<std::string>("ui.map.fleet.button.rollover.sound.path"), true); }
382 
PlayFleetButtonOpenSound()383 void FleetButton::PlayFleetButtonOpenSound()
384 { Sound::GetSound().PlaySound(GetOptionsDB().Get<std::string>("ui.map.fleet.button.press.sound.path"), true); }
385 
386 /////////////////////
387 // Free Functions
388 /////////////////////
FleetHeadIcons(std::shared_ptr<const Fleet> fleet,FleetButton::SizeType size_type)389 std::vector<std::shared_ptr<GG::Texture>> FleetHeadIcons(std::shared_ptr<const Fleet> fleet, FleetButton::SizeType size_type) {
390     std::vector<std::shared_ptr<const Fleet>> fleets(1U, fleet);
391     return FleetHeadIcons(fleets, size_type);
392 }
393 
FleetHeadIcons(const std::vector<std::shared_ptr<const Fleet>> & fleets,FleetButton::SizeType size_type)394 std::vector<std::shared_ptr<GG::Texture>> FleetHeadIcons(const std::vector<std::shared_ptr<const Fleet>>& fleets, FleetButton::SizeType size_type) {
395     if (size_type == FleetButton::SizeType::NONE || size_type == FleetButton::SizeType::TINY)
396         return std::vector<std::shared_ptr<GG::Texture>>();
397 
398     // get file name prefix for appropriate size of icon
399     std::string size_prefix = FleetIconSizePrefix(size_type);
400     if (size_prefix.empty())
401         return std::vector<std::shared_ptr<GG::Texture>>();
402 
403     // the set of fleets is treated like a fleet that contains all the ships
404     bool hasColonyShips = false; bool hasOutpostShips = false; bool hasTroopShips = false; bool hasMonsters = false; bool hasArmedShips = false;
405     for (auto& fleet : fleets) {
406         if (!fleet)
407             continue;
408 
409         hasColonyShips  = hasColonyShips  || fleet->HasColonyShips();
410         hasOutpostShips = hasOutpostShips || fleet->HasOutpostShips();
411         hasTroopShips   = hasTroopShips   || fleet->HasTroopShips();
412         hasMonsters     = hasMonsters     || fleet->HasMonsters();
413         hasArmedShips   = hasArmedShips   || fleet->HasArmedShips() || fleet->HasFighterShips();
414     }
415 
416     // get file name main part depending on type of fleet
417     // symbol type prioritized by the ship type arbitrarily deemed "most important"
418     std::vector<std::string> main_filenames;
419     if (hasMonsters) {
420         if (hasArmedShips)   { main_filenames.push_back("head-monster.png"); }
421         else                 { main_filenames.push_back("head-monster-harmless.png"); }
422     } else {
423         main_filenames.reserve(4);
424         if (hasArmedShips)   { main_filenames.push_back("head-warship.png"); }
425         if (hasColonyShips)  { main_filenames.push_back("head-colony.png");  }
426         if (hasOutpostShips) { main_filenames.push_back("head-outpost.png"); }
427         if (hasTroopShips)   { main_filenames.push_back("head-lander.png");  }
428     }
429     if (main_filenames.empty()) { main_filenames.push_back("head-scout.png"); }
430 
431     std::vector<std::shared_ptr<GG::Texture>> result;
432     result.reserve(main_filenames.size());
433     for (const std::string& name : main_filenames) {
434         std::shared_ptr<GG::Texture> texture_temp = ClientUI::GetTexture(
435             ClientUI::ArtDir() / "icons" / "fleet" / (size_prefix + name), false);
436         glBindTexture(GL_TEXTURE_2D, texture_temp->OpenGLId());
437         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
438         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
439         glBindTexture(GL_TEXTURE_2D, 0);
440 
441         result.push_back(texture_temp);
442     }
443 
444     return result;
445 }
446 
FleetSizeIcon(std::shared_ptr<const Fleet> fleet,FleetButton::SizeType size_type)447 std::shared_ptr<GG::Texture> FleetSizeIcon(std::shared_ptr<const Fleet> fleet, FleetButton::SizeType size_type) {
448     if (!fleet)
449         return FleetSizeIcon(1u, size_type);
450     return FleetSizeIcon(fleet->NumShips(), size_type);
451 }
452 
FleetSizeIcon(unsigned int fleet_size,FleetButton::SizeType size_type)453 std::shared_ptr<GG::Texture> FleetSizeIcon(unsigned int fleet_size, FleetButton::SizeType size_type) {
454     if (fleet_size < 1u)
455         fleet_size = 1u; // because there's no zero-ship icon, and the one-ship icon is (as of this writing) blank, so is fitting for zero ships
456 
457     if (size_type == FleetButton::SizeType::NONE)
458         return nullptr;
459 
460     if (size_type == FleetButton::SizeType::TINY) {
461         if (fleet_size > 1u)
462             return ClientUI::GetTexture(ClientUI::ArtDir() / "icons" / "fleet" / "tiny-fleet-multi.png", false);
463         else
464             return ClientUI::GetTexture(ClientUI::ArtDir() / "icons" / "fleet" / "tiny-fleet.png", false);
465     }
466 
467     std::string size_prefix = FleetIconSizePrefix(size_type);
468 
469     if (size_prefix.empty())
470         return nullptr;
471 
472     std::shared_ptr<GG::Texture> texture_temp = ClientUI::GetClientUI()->GetModuloTexture(
473         ClientUI::ArtDir() / "icons" / "fleet", (size_prefix + "tail-"), FleetSizeIconNumber(fleet_size), false);
474     glBindTexture(GL_TEXTURE_2D, texture_temp->OpenGLId());
475     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
476     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
477     glBindTexture(GL_TEXTURE_2D, 0);
478 
479     return texture_temp;
480 }
481 
FleetBlockadedIcon(FleetButton::SizeType size_type)482 std::shared_ptr<GG::Texture> FleetBlockadedIcon(FleetButton::SizeType size_type) {
483     if (size_type == FleetButton::SizeType::NONE)
484         return nullptr;
485 
486     std::string size_prefix = FleetIconSizePrefix(size_type);
487     if (size_type == FleetButton::SizeType::TINY)
488         size_prefix = "tiny-";
489 
490     std::shared_ptr<GG::Texture> retval = ClientUI::GetClientUI()->GetTexture(ClientUI::ArtDir() / "icons" / "fleet" / (size_prefix + "blockade.png"), false);
491     glBindTexture(GL_TEXTURE_2D, retval->OpenGLId());
492     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
493     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
494     glBindTexture(GL_TEXTURE_2D, 0);
495 
496     return retval;
497 }
498 
FleetSelectionIndicatorIcon()499 std::shared_ptr<GG::Texture> FleetSelectionIndicatorIcon() {
500     static std::shared_ptr<GG::Texture> retval;
501     if (!retval) {
502         retval = ClientUI::GetClientUI()->GetTexture(ClientUI::ArtDir() / "icons" / "fleet" / "fleet_selection.png", false);
503         glBindTexture(GL_TEXTURE_2D, retval->OpenGLId());
504         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
505         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
506         glBindTexture(GL_TEXTURE_2D, 0);
507     }
508     return retval;
509 }
510