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
10CLayerQuads::CLayerQuads(CEditor *pEditor) :
11 CLayer(pEditor)
12{
13 m_Type = LAYERTYPE_QUADS;
14 m_aName[0] = '\0';
15 m_Image = -1;
16}
17
18CLayerQuads::CLayerQuads(const CLayerQuads &Other) :
19 CLayer(Other)
20{
21 m_Image = Other.m_Image;
22 m_vQuads = Other.m_vQuads;
23}
24
25CLayerQuads::~CLayerQuads() = default;
26
27void 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
39CQuad *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
97void 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
111int 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
141void 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
162void 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
172void 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
182void 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
190void 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
209void 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
224CUi::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)
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
255void CLayerQuads::ModifyImageIndex(FIndexModifyFunction Func)
256{
257 Func(&m_Image);
258}
259
260void 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
269std::shared_ptr<CLayer> CLayerQuads::Duplicate() const
270{
271 return std::make_shared<CLayerQuads>(args: *this);
272}
273
274int 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
287const char *CLayerQuads::TypeName() const
288{
289 return "quads";
290}
291