1 | /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ |
2 | /* If you are missing that file, acquire a complete release at teeworlds.com. */ |
3 | #include "layer_quads.h" |
4 | |
5 | #include <game/editor/editor.h> |
6 | #include <game/editor/editor_actions.h> |
7 | |
8 | #include "image.h" |
9 | |
10 | CLayerQuads::CLayerQuads(CEditor *pEditor) : |
11 | CLayer(pEditor) |
12 | { |
13 | m_Type = LAYERTYPE_QUADS; |
14 | m_aName[0] = '\0'; |
15 | m_Image = -1; |
16 | } |
17 | |
18 | CLayerQuads::CLayerQuads(const CLayerQuads &Other) : |
19 | CLayer(Other) |
20 | { |
21 | m_Image = Other.m_Image; |
22 | m_vQuads = Other.m_vQuads; |
23 | } |
24 | |
25 | CLayerQuads::~CLayerQuads() = default; |
26 | |
27 | void CLayerQuads::Render(bool QuadPicker) |
28 | { |
29 | Graphics()->TextureClear(); |
30 | if(m_Image >= 0 && (size_t)m_Image < m_pEditor->m_Map.m_vpImages.size()) |
31 | Graphics()->TextureSet(Texture: m_pEditor->m_Map.m_vpImages[m_Image]->m_Texture); |
32 | |
33 | Graphics()->BlendNone(); |
34 | m_pEditor->RenderTools()->ForceRenderQuads(pQuads: m_vQuads.data(), NumQuads: m_vQuads.size(), Flags: LAYERRENDERFLAG_OPAQUE, pfnEval: CEditor::EnvelopeEval, pUser: m_pEditor); |
35 | Graphics()->BlendNormal(); |
36 | m_pEditor->RenderTools()->ForceRenderQuads(pQuads: m_vQuads.data(), NumQuads: m_vQuads.size(), Flags: LAYERRENDERFLAG_TRANSPARENT, pfnEval: CEditor::EnvelopeEval, pUser: m_pEditor); |
37 | } |
38 | |
39 | CQuad *CLayerQuads::NewQuad(int x, int y, int Width, int Height) |
40 | { |
41 | m_pEditor->m_Map.OnModify(); |
42 | |
43 | m_vQuads.emplace_back(); |
44 | CQuad *pQuad = &m_vQuads[m_vQuads.size() - 1]; |
45 | |
46 | pQuad->m_PosEnv = -1; |
47 | pQuad->m_ColorEnv = -1; |
48 | pQuad->m_PosEnvOffset = 0; |
49 | pQuad->m_ColorEnvOffset = 0; |
50 | |
51 | Width /= 2; |
52 | Height /= 2; |
53 | pQuad->m_aPoints[0].x = i2fx(v: x - Width); |
54 | pQuad->m_aPoints[0].y = i2fx(v: y - Height); |
55 | pQuad->m_aPoints[1].x = i2fx(v: x + Width); |
56 | pQuad->m_aPoints[1].y = i2fx(v: y - Height); |
57 | pQuad->m_aPoints[2].x = i2fx(v: x - Width); |
58 | pQuad->m_aPoints[2].y = i2fx(v: y + Height); |
59 | pQuad->m_aPoints[3].x = i2fx(v: x + Width); |
60 | pQuad->m_aPoints[3].y = i2fx(v: y + Height); |
61 | |
62 | pQuad->m_aPoints[4].x = i2fx(v: x); // pivot |
63 | pQuad->m_aPoints[4].y = i2fx(v: y); |
64 | |
65 | pQuad->m_aTexcoords[0].x = i2fx(v: 0); |
66 | pQuad->m_aTexcoords[0].y = i2fx(v: 0); |
67 | |
68 | pQuad->m_aTexcoords[1].x = i2fx(v: 1); |
69 | pQuad->m_aTexcoords[1].y = i2fx(v: 0); |
70 | |
71 | pQuad->m_aTexcoords[2].x = i2fx(v: 0); |
72 | pQuad->m_aTexcoords[2].y = i2fx(v: 1); |
73 | |
74 | pQuad->m_aTexcoords[3].x = i2fx(v: 1); |
75 | pQuad->m_aTexcoords[3].y = i2fx(v: 1); |
76 | |
77 | pQuad->m_aColors[0].r = 255; |
78 | pQuad->m_aColors[0].g = 255; |
79 | pQuad->m_aColors[0].b = 255; |
80 | pQuad->m_aColors[0].a = 255; |
81 | pQuad->m_aColors[1].r = 255; |
82 | pQuad->m_aColors[1].g = 255; |
83 | pQuad->m_aColors[1].b = 255; |
84 | pQuad->m_aColors[1].a = 255; |
85 | pQuad->m_aColors[2].r = 255; |
86 | pQuad->m_aColors[2].g = 255; |
87 | pQuad->m_aColors[2].b = 255; |
88 | pQuad->m_aColors[2].a = 255; |
89 | pQuad->m_aColors[3].r = 255; |
90 | pQuad->m_aColors[3].g = 255; |
91 | pQuad->m_aColors[3].b = 255; |
92 | pQuad->m_aColors[3].a = 255; |
93 | |
94 | return pQuad; |
95 | } |
96 | |
97 | void CLayerQuads::BrushSelecting(CUIRect Rect) |
98 | { |
99 | // draw selection rectangle |
100 | IGraphics::CLineItem Array[4] = { |
101 | IGraphics::CLineItem(Rect.x, Rect.y, Rect.x + Rect.w, Rect.y), |
102 | IGraphics::CLineItem(Rect.x + Rect.w, Rect.y, Rect.x + Rect.w, Rect.y + Rect.h), |
103 | IGraphics::CLineItem(Rect.x + Rect.w, Rect.y + Rect.h, Rect.x, Rect.y + Rect.h), |
104 | IGraphics::CLineItem(Rect.x, Rect.y + Rect.h, Rect.x, Rect.y)}; |
105 | Graphics()->TextureClear(); |
106 | Graphics()->LinesBegin(); |
107 | Graphics()->LinesDraw(pArray: Array, Num: 4); |
108 | Graphics()->LinesEnd(); |
109 | } |
110 | |
111 | int CLayerQuads::BrushGrab(std::shared_ptr<CLayerGroup> pBrush, CUIRect Rect) |
112 | { |
113 | // create new layers |
114 | std::shared_ptr<CLayerQuads> pGrabbed = std::make_shared<CLayerQuads>(args&: m_pEditor); |
115 | pGrabbed->m_Image = m_Image; |
116 | pBrush->AddLayer(pLayer: pGrabbed); |
117 | |
118 | //dbg_msg("", "%f %f %f %f", rect.x, rect.y, rect.w, rect.h); |
119 | for(const auto &Quad : m_vQuads) |
120 | { |
121 | float px = fx2f(v: Quad.m_aPoints[4].x); |
122 | float py = fx2f(v: Quad.m_aPoints[4].y); |
123 | |
124 | if(px > Rect.x && px < Rect.x + Rect.w && py > Rect.y && py < Rect.y + Rect.h) |
125 | { |
126 | CQuad n = Quad; |
127 | |
128 | for(auto &Point : n.m_aPoints) |
129 | { |
130 | Point.x -= f2fx(v: Rect.x); |
131 | Point.y -= f2fx(v: Rect.y); |
132 | } |
133 | |
134 | pGrabbed->m_vQuads.push_back(x: n); |
135 | } |
136 | } |
137 | |
138 | return pGrabbed->m_vQuads.empty() ? 0 : 1; |
139 | } |
140 | |
141 | void CLayerQuads::BrushPlace(std::shared_ptr<CLayer> pBrush, float wx, float wy) |
142 | { |
143 | std::shared_ptr<CLayerQuads> pQuadLayer = std::static_pointer_cast<CLayerQuads>(r: pBrush); |
144 | std::vector<CQuad> vAddedQuads; |
145 | for(const auto &Quad : pQuadLayer->m_vQuads) |
146 | { |
147 | CQuad n = Quad; |
148 | |
149 | for(auto &Point : n.m_aPoints) |
150 | { |
151 | Point.x += f2fx(v: wx); |
152 | Point.y += f2fx(v: wy); |
153 | } |
154 | |
155 | m_vQuads.push_back(x: n); |
156 | vAddedQuads.push_back(x: n); |
157 | } |
158 | m_pEditor->m_EditorHistory.RecordAction(pAction: std::make_shared<CEditorActionQuadPlace>(args&: m_pEditor, args&: m_pEditor->m_SelectedGroup, args&: m_pEditor->m_vSelectedLayers[0], args&: vAddedQuads)); |
159 | m_pEditor->m_Map.OnModify(); |
160 | } |
161 | |
162 | void CLayerQuads::BrushFlipX() |
163 | { |
164 | for(auto &Quad : m_vQuads) |
165 | { |
166 | std::swap(a&: Quad.m_aPoints[0], b&: Quad.m_aPoints[1]); |
167 | std::swap(a&: Quad.m_aPoints[2], b&: Quad.m_aPoints[3]); |
168 | } |
169 | m_pEditor->m_Map.OnModify(); |
170 | } |
171 | |
172 | void CLayerQuads::BrushFlipY() |
173 | { |
174 | for(auto &Quad : m_vQuads) |
175 | { |
176 | std::swap(a&: Quad.m_aPoints[0], b&: Quad.m_aPoints[2]); |
177 | std::swap(a&: Quad.m_aPoints[1], b&: Quad.m_aPoints[3]); |
178 | } |
179 | m_pEditor->m_Map.OnModify(); |
180 | } |
181 | |
182 | void Rotate(vec2 *pCenter, vec2 *pPoint, float Rotation) |
183 | { |
184 | float x = pPoint->x - pCenter->x; |
185 | float y = pPoint->y - pCenter->y; |
186 | pPoint->x = x * std::cos(x: Rotation) - y * std::sin(x: Rotation) + pCenter->x; |
187 | pPoint->y = x * std::sin(x: Rotation) + y * std::cos(x: Rotation) + pCenter->y; |
188 | } |
189 | |
190 | void CLayerQuads::BrushRotate(float Amount) |
191 | { |
192 | vec2 Center; |
193 | GetSize(pWidth: &Center.x, pHeight: &Center.y); |
194 | Center.x /= 2; |
195 | Center.y /= 2; |
196 | |
197 | for(auto &Quad : m_vQuads) |
198 | { |
199 | for(auto &Point : Quad.m_aPoints) |
200 | { |
201 | vec2 Pos(fx2f(v: Point.x), fx2f(v: Point.y)); |
202 | Rotate(pCenter: &Center, pPoint: &Pos, Rotation: Amount); |
203 | Point.x = f2fx(v: Pos.x); |
204 | Point.y = f2fx(v: Pos.y); |
205 | } |
206 | } |
207 | } |
208 | |
209 | void CLayerQuads::GetSize(float *pWidth, float *pHeight) |
210 | { |
211 | *pWidth = 0; |
212 | *pHeight = 0; |
213 | |
214 | for(const auto &Quad : m_vQuads) |
215 | { |
216 | for(const auto &Point : Quad.m_aPoints) |
217 | { |
218 | *pWidth = maximum(a: *pWidth, b: fx2f(v: Point.x)); |
219 | *pHeight = maximum(a: *pHeight, b: fx2f(v: Point.y)); |
220 | } |
221 | } |
222 | } |
223 | |
224 | CUi::EPopupMenuFunctionResult CLayerQuads::RenderProperties(CUIRect *pToolBox) |
225 | { |
226 | CProperty aProps[] = { |
227 | {.m_pName: "Image" , .m_Value: m_Image, .m_Type: PROPTYPE_IMAGE, .m_Min: -1, .m_Max: 0}, |
228 | {.m_pName: nullptr}, |
229 | }; |
230 | |
231 | static int s_aIds[(int)ELayerQuadsProp::NUM_PROPS] = {0}; |
232 | int NewVal = 0; |
233 | auto [State, Prop] = m_pEditor->DoPropertiesWithState<ELayerQuadsProp>(pToolbox: pToolBox, pProps: aProps, pIds: s_aIds, pNewVal: &NewVal); |
234 | if(Prop != ELayerQuadsProp::PROP_NONE && (State == EEditState::END || State == EEditState::ONE_GO)) |
235 | { |
236 | m_pEditor->m_Map.OnModify(); |
237 | } |
238 | |
239 | static CLayerQuadsPropTracker s_Tracker(m_pEditor); |
240 | s_Tracker.Begin(pObject: this, Prop, State); |
241 | |
242 | if(Prop == ELayerQuadsProp::PROP_IMAGE) |
243 | { |
244 | if(NewVal >= 0) |
245 | m_Image = NewVal % m_pEditor->m_Map.m_vpImages.size(); |
246 | else |
247 | m_Image = -1; |
248 | } |
249 | |
250 | s_Tracker.End(Prop, State); |
251 | |
252 | return CUi::POPUP_KEEP_OPEN; |
253 | } |
254 | |
255 | void CLayerQuads::ModifyImageIndex(FIndexModifyFunction Func) |
256 | { |
257 | Func(&m_Image); |
258 | } |
259 | |
260 | void CLayerQuads::ModifyEnvelopeIndex(FIndexModifyFunction Func) |
261 | { |
262 | for(auto &Quad : m_vQuads) |
263 | { |
264 | Func(&Quad.m_PosEnv); |
265 | Func(&Quad.m_ColorEnv); |
266 | } |
267 | } |
268 | |
269 | std::shared_ptr<CLayer> CLayerQuads::Duplicate() const |
270 | { |
271 | return std::make_shared<CLayerQuads>(args: *this); |
272 | } |
273 | |
274 | int CLayerQuads::SwapQuads(int Index0, int Index1) |
275 | { |
276 | if(Index0 < 0 || Index0 >= (int)m_vQuads.size()) |
277 | return Index0; |
278 | if(Index1 < 0 || Index1 >= (int)m_vQuads.size()) |
279 | return Index0; |
280 | if(Index0 == Index1) |
281 | return Index0; |
282 | m_pEditor->m_Map.OnModify(); |
283 | std::swap(a&: m_vQuads[Index0], b&: m_vQuads[Index1]); |
284 | return Index1; |
285 | } |
286 | |
287 | const char *CLayerQuads::TypeName() const |
288 | { |
289 | return "quads" ; |
290 | } |
291 | |