1 /*
2  * Copyright (C) 2006-2020 by the Widelands Development Team
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  */
19 
20 #include "graphic/gl/fields_to_draw.h"
21 
22 #include "graphic/gl/coordinate_conversion.h"
23 #include "logic/map_objects/world/terrain_description.h"
24 #include "wui/mapviewpixelfunctions.h"
25 
26 /*
27  * Explanation of how drawing works:
28  * Schematic of triangle neighborhood:
29  *
30  *               *
31  *              / \
32  *             / u \
33  *         (f)/     \
34  *    *------*------* (r)
35  *     \  l / \  r / \
36  *      \  /   \  /   \
37  *       \/  d  \/ rr  \
38  *       *------*------* (br)
39  *        \ dd /
40  *         \  /
41  *          \/
42  *          *
43  *
44  * Each field (f) owns two triangles: (r)ight & (d)own. When we look at the
45  * field, we have to make sure to schedule drawing the triangles. This is done
46  * by TerrainProgram.
47  *
48  * To draw dithered edges, we have to look at the neighboring triangles for the
49  * two triangles too: If a neighboring triangle has another texture and our
50  * dither layer is smaller, we have to draw a dithering triangle too - this lets
51  * the neighboring texture bleed into our triangle.
52  *
53  * The dither triangle is the triangle that should be partially drawn (either r or
54  * d). Example: if r and d have different textures and r.dither_layer >
55  * d.dither_layer, then we will repaint d with the dither texture as mask.
56  */
57 
58 namespace {
59 
60 // Returns the brightness value in [0, 1.] for 'fcoords'.
field_brightness(const Widelands::FCoords & fcoords)61 float field_brightness(const Widelands::FCoords& fcoords) {
62 	uint32_t brightness = 144 + fcoords.field->get_brightness();
63 	brightness = std::min<uint32_t>(255, (brightness * 255) / 160);
64 	return brightness / 255.;
65 }
66 
67 }  // namespace
68 
reset(const Widelands::EditorGameBase & egbase,const Vector2f & viewpoint,const float zoom,RenderTarget * dst)69 void FieldsToDraw::reset(const Widelands::EditorGameBase& egbase,
70                          const Vector2f& viewpoint,
71                          const float zoom,
72                          RenderTarget* dst) {
73 	assert(viewpoint.x >= 0);  // divisions involving negative numbers are bad
74 	assert(viewpoint.y >= 0);
75 	assert(dst->get_offset().x <= 0);
76 	assert(dst->get_offset().y <= 0);
77 
78 	min_fx_ = std::floor(viewpoint.x / kTriangleWidth);
79 	min_fy_ = std::floor(viewpoint.y / kTriangleHeight);
80 
81 	// If a view window is partially moved outside of the display, its 'rect' is
82 	// adjusted to be fully contained on the screen - i.e. x = 0 and width is
83 	// made smaller. Its offset is made negative to account for this change.
84 	// To figure out which fields we need to draw, we have to add the absolute
85 	// value of 'offset' to the actual dimension of the 'rect' to get to desired
86 	// dimension of the 'rect'
87 	const Vector2f br_map = MapviewPixelFunctions::panel_to_map(
88 	   viewpoint, zoom, Vector2f(dst->get_rect().w + std::abs(dst->get_offset().x),
89 	                             dst->get_rect().h + std::abs(dst->get_offset().y)));
90 	max_fx_ = std::ceil(br_map.x / kTriangleWidth);
91 	max_fy_ = std::ceil(br_map.y / kTriangleHeight);
92 
93 	// Adjust for triangle boundary effects and for height differences.
94 	min_fx_ -= 2;
95 	max_fx_ += 2;
96 	min_fy_ -= 2;
97 	max_fy_ += 10;
98 
99 	const auto& surface = dst->get_surface();
100 	const int surface_width = surface.width();
101 	const int surface_height = surface.height();
102 
103 	const Widelands::Map& map = egbase.map();
104 
105 	w_ = max_fx_ - min_fx_ + 1;
106 	h_ = max_fy_ - min_fy_ + 1;
107 	assert(w_ > 0);
108 	assert(h_ > 0);
109 
110 	// Ensure that there is enough memory for the resize operation
111 	size_t dimension = w_ * h_;
112 	const size_t max_dimension = fields_.max_size();
113 	if (dimension > max_dimension) {
114 		log("WARNING: Not enough memory allocated to redraw the whole map!\nWe recommend that you "
115 		    "restart Widelands\n");
116 		dimension = max_dimension;
117 	}
118 	// Now resize the vector
119 	if (fields_.size() != dimension) {
120 		fields_.resize(dimension);
121 	}
122 
123 	for (int32_t fy = min_fy_; fy <= max_fy_; ++fy) {
124 		for (int32_t fx = min_fx_; fx <= max_fx_; ++fx) {
125 			FieldsToDraw::Field& f = fields_[calculate_index(fx, fy)];
126 
127 			f.geometric_coords = Widelands::Coords(fx, fy);
128 
129 			f.ln_index = calculate_index(fx - 1, fy);
130 			f.rn_index = calculate_index(fx + 1, fy);
131 			f.trn_index = calculate_index(fx + (fy & 1), fy - 1);
132 			f.bln_index = calculate_index(fx + (fy & 1) - 1, fy + 1);
133 			f.brn_index = calculate_index(fx + (fy & 1), fy + 1);
134 
135 			// Texture coordinates for pseudo random tiling of terrain and road
136 			// graphics. Since screen space X increases top-to-bottom and OpenGL
137 			// increases bottom-to-top we flip the y coordinate to not have
138 			// terrains and road graphics vertically mirrorerd.
139 			Vector2f map_pixel =
140 			   MapviewPixelFunctions::to_map_pixel_ignoring_height(f.geometric_coords);
141 			f.texture_coords.x = map_pixel.x / Widelands::kTextureSideLength;
142 			f.texture_coords.y = -map_pixel.y / Widelands::kTextureSideLength;
143 
144 			Widelands::Coords normalized = f.geometric_coords;
145 			map.normalize_coords(normalized);
146 			f.fcoords = map.get_fcoords(normalized);
147 
148 			map_pixel.y -= f.fcoords.field->get_height() * kHeightFactor;
149 
150 			f.rendertarget_pixel = MapviewPixelFunctions::map_to_panel(viewpoint, zoom, map_pixel);
151 			f.gl_position = f.surface_pixel = f.rendertarget_pixel +
152 			                                  dst->get_rect().origin().cast<float>() +
153 			                                  dst->get_offset().cast<float>();
154 			pixel_to_gl_renderbuffer(
155 			   surface_width, surface_height, &f.gl_position.x, &f.gl_position.y);
156 
157 			f.brightness = field_brightness(f.fcoords);
158 
159 			const Widelands::PlayerNumber owned_by = f.fcoords.field->get_owned_by();
160 			f.owner = owned_by != 0 ? egbase.get_player(owned_by) : nullptr;
161 			f.is_border = f.fcoords.field->is_border();
162 			f.vision = 2;
163 			f.road_e = f.fcoords.field->get_road(Widelands::WALK_E);
164 			f.road_se = f.fcoords.field->get_road(Widelands::WALK_SE);
165 			f.road_sw = f.fcoords.field->get_road(Widelands::WALK_SW);
166 		}
167 	}
168 }
169