1 // Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details
2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
3 
4 #include "Thruster.h"
5 #include "BaseLoader.h"
6 #include "Easing.h"
7 #include "NodeVisitor.h"
8 #include "Serializer.h"
9 #include "graphics/Material.h"
10 #include "graphics/RenderState.h"
11 #include "graphics/Renderer.h"
12 #include "graphics/TextureBuilder.h"
13 #include "graphics/VertexArray.h"
14 
15 namespace SceneGraph {
16 
17 	static const std::string thrusterTextureFilename("textures/thruster.dds");
18 	static const std::string thrusterGlowTextureFilename("textures/halo.dds");
19 	static Color baseColor(178, 153, 255, 255);
20 
Thruster(Graphics::Renderer * r,bool _linear,const vector3f & _pos,const vector3f & _dir)21 	Thruster::Thruster(Graphics::Renderer *r, bool _linear, const vector3f &_pos, const vector3f &_dir) :
22 		Node(r, NODE_TRANSPARENT),
23 		linearOnly(_linear),
24 		dir(_dir),
25 		pos(_pos),
26 		currentColor(baseColor)
27 	{
28 		//set up materials
29 		Graphics::MaterialDescriptor desc;
30 		desc.textures = 1;
31 
32 		m_tMat.Reset(r->CreateMaterial(desc));
33 		m_tMat->texture0 = Graphics::TextureBuilder::Billboard(thrusterTextureFilename).GetOrCreateTexture(r, "billboard");
34 		m_tMat->diffuse = baseColor;
35 
36 		m_glowMat.Reset(r->CreateMaterial(desc));
37 		m_glowMat->texture0 = Graphics::TextureBuilder::Billboard(thrusterGlowTextureFilename).GetOrCreateTexture(r, "billboard");
38 		m_glowMat->diffuse = baseColor;
39 
40 		Graphics::RenderStateDesc rsd;
41 		rsd.blendMode = Graphics::BLEND_ALPHA_ONE;
42 		rsd.depthWrite = false;
43 		rsd.cullMode = Graphics::CULL_NONE;
44 		m_renderState = r->CreateRenderState(rsd);
45 	}
46 
Thruster(const Thruster & thruster,NodeCopyCache * cache)47 	Thruster::Thruster(const Thruster &thruster, NodeCopyCache *cache) :
48 		Node(thruster, cache),
49 		m_tMat(thruster.m_tMat),
50 		m_renderState(thruster.m_renderState),
51 		linearOnly(thruster.linearOnly),
52 		dir(thruster.dir),
53 		pos(thruster.pos),
54 		currentColor(thruster.currentColor)
55 	{
56 	}
57 
Clone(NodeCopyCache * cache)58 	Node *Thruster::Clone(NodeCopyCache *cache)
59 	{
60 		return this; //thrusters are shared
61 	}
62 
Accept(NodeVisitor & nv)63 	void Thruster::Accept(NodeVisitor &nv)
64 	{
65 		nv.ApplyThruster(*this);
66 	}
67 
Render(const matrix4x4f & trans,const RenderData * rd)68 	void Thruster::Render(const matrix4x4f &trans, const RenderData *rd)
69 	{
70 		PROFILE_SCOPED()
71 		float power = -dir.Dot(vector3f(rd->linthrust));
72 
73 		if (!linearOnly) {
74 			// pitch X
75 			// yaw   Y
76 			// roll  Z
77 			//model center is at 0,0,0, no need for invSubModelMat stuff
78 			const vector3f at = vector3f(rd->angthrust);
79 			const vector3f angdir = pos.Cross(dir);
80 
81 			const float xp = angdir.x * at.x;
82 			const float yp = angdir.y * at.y;
83 			const float zp = angdir.z * at.z;
84 
85 			if (xp + yp + zp > 0) {
86 				if (xp > yp && xp > zp && fabs(at.x) > power)
87 					power = fabs(at.x);
88 				else if (yp > xp && yp > zp && fabs(at.y) > power)
89 					power = fabs(at.y);
90 				else if (zp > xp && zp > yp && fabs(at.z) > power)
91 					power = fabs(at.z);
92 			}
93 		}
94 		if (power < 0.001f) return;
95 
96 		m_tMat->diffuse = m_glowMat->diffuse = currentColor * power;
97 
98 		//directional fade
99 		vector3f cdir = vector3f(trans * -dir).Normalized();
100 		vector3f vdir = vector3f(trans[2], trans[6], -trans[10]).Normalized();
101 		// XXX check this for transition to new colors.
102 		m_glowMat->diffuse.a = Easing::Circ::EaseIn(Clamp(vdir.Dot(cdir), 0.f, 1.f), 0.f, 1.f, 1.f) * 255;
103 		m_tMat->diffuse.a = 255 - m_glowMat->diffuse.a;
104 
105 		Graphics::Renderer *r = GetRenderer();
106 		if (!m_tBuffer.Valid()) {
107 			m_tBuffer.Reset(CreateThrusterGeometry(r, m_tMat.Get()));
108 			m_glowBuffer.Reset(CreateGlowGeometry(r, m_glowMat.Get()));
109 		}
110 
111 		r->SetTransform(trans);
112 		r->DrawBuffer(m_tBuffer.Get(), m_renderState, m_tMat.Get());
113 		r->DrawBuffer(m_glowBuffer.Get(), m_renderState, m_glowMat.Get());
114 	}
115 
Save(NodeDatabase & db)116 	void Thruster::Save(NodeDatabase &db)
117 	{
118 		Node::Save(db);
119 		db.wr->Bool(linearOnly);
120 		db.wr->Vector3f(dir);
121 		db.wr->Vector3f(pos);
122 	}
123 
Load(NodeDatabase & db)124 	Thruster *Thruster::Load(NodeDatabase &db)
125 	{
126 		const bool linear = db.rd->Bool();
127 		const vector3f dir = db.rd->Vector3f();
128 		const vector3f pos = db.rd->Vector3f();
129 		Thruster *t = new Thruster(db.loader->GetRenderer(), linear, pos, dir);
130 		return t;
131 	}
132 
CreateThrusterGeometry(Graphics::Renderer * r,Graphics::Material * mat)133 	Graphics::VertexBuffer *Thruster::CreateThrusterGeometry(Graphics::Renderer *r, Graphics::Material *mat)
134 	{
135 		Graphics::VertexArray verts(Graphics::ATTRIB_POSITION | Graphics::ATTRIB_UV0);
136 
137 		//zero at thruster center
138 		//+x down
139 		//+y right
140 		//+z backwards (or thrust direction)
141 		const float w = 0.5f;
142 
143 		vector3f one(0.f, -w, 0.f); //top left
144 		vector3f two(0.f, w, 0.f); //top right
145 		vector3f three(0.f, w, 1.f); //bottom right
146 		vector3f four(0.f, -w, 1.f); //bottom left
147 
148 		//uv coords
149 		const vector2f topLeft(0.f, 1.f);
150 		const vector2f topRight(1.f, 1.f);
151 		const vector2f botLeft(0.f, 0.f);
152 		const vector2f botRight(1.f, 0.f);
153 
154 		//add four intersecting planes to create a volumetric effect
155 		for (int i = 0; i < 4; i++) {
156 			verts.Add(one, topLeft);
157 			verts.Add(two, topRight);
158 			verts.Add(three, botRight);
159 
160 			verts.Add(three, botRight);
161 			verts.Add(four, botLeft);
162 			verts.Add(one, topLeft);
163 
164 			one.ArbRotate(vector3f(0.f, 0.f, 1.f), DEG2RAD(45.f));
165 			two.ArbRotate(vector3f(0.f, 0.f, 1.f), DEG2RAD(45.f));
166 			three.ArbRotate(vector3f(0.f, 0.f, 1.f), DEG2RAD(45.f));
167 			four.ArbRotate(vector3f(0.f, 0.f, 1.f), DEG2RAD(45.f));
168 		}
169 
170 		//create buffer and upload data
171 		Graphics::VertexBufferDesc vbd;
172 		vbd.attrib[0].semantic = Graphics::ATTRIB_POSITION;
173 		vbd.attrib[0].format = Graphics::ATTRIB_FORMAT_FLOAT3;
174 		vbd.attrib[1].semantic = Graphics::ATTRIB_UV0;
175 		vbd.attrib[1].format = Graphics::ATTRIB_FORMAT_FLOAT2;
176 		vbd.numVertices = verts.GetNumVerts();
177 		vbd.usage = Graphics::BUFFER_USAGE_STATIC;
178 		Graphics::VertexBuffer *vb = r->CreateVertexBuffer(vbd);
179 		vb->Populate(verts);
180 
181 		return vb;
182 	}
183 
CreateGlowGeometry(Graphics::Renderer * r,Graphics::Material * mat)184 	Graphics::VertexBuffer *Thruster::CreateGlowGeometry(Graphics::Renderer *r, Graphics::Material *mat)
185 	{
186 		Graphics::VertexArray verts(Graphics::ATTRIB_POSITION | Graphics::ATTRIB_UV0);
187 
188 		//create glow billboard for linear thrusters
189 		const float w = 0.2;
190 
191 		vector3f one(-w, -w, 0.f); //top left
192 		vector3f two(-w, w, 0.f); //top right
193 		vector3f three(w, w, 0.f); //bottom right
194 		vector3f four(w, -w, 0.f); //bottom left
195 
196 		//uv coords
197 		const vector2f topLeft(0.f, 1.f);
198 		const vector2f topRight(1.f, 1.f);
199 		const vector2f botLeft(0.f, 0.f);
200 		const vector2f botRight(1.f, 0.f);
201 
202 		for (int i = 0; i < 5; i++) {
203 			verts.Add(one, topLeft);
204 			verts.Add(two, topRight);
205 			verts.Add(three, botRight);
206 
207 			verts.Add(three, botRight);
208 			verts.Add(four, botLeft);
209 			verts.Add(one, topLeft);
210 
211 			one.z += .1f;
212 			two.z = three.z = four.z = one.z;
213 		}
214 
215 		//create buffer and upload data
216 		Graphics::VertexBufferDesc vbd;
217 		vbd.attrib[0].semantic = Graphics::ATTRIB_POSITION;
218 		vbd.attrib[0].format = Graphics::ATTRIB_FORMAT_FLOAT3;
219 		vbd.attrib[1].semantic = Graphics::ATTRIB_UV0;
220 		vbd.attrib[1].format = Graphics::ATTRIB_FORMAT_FLOAT2;
221 		vbd.numVertices = verts.GetNumVerts();
222 		vbd.usage = Graphics::BUFFER_USAGE_STATIC;
223 		Graphics::VertexBuffer *vb = r->CreateVertexBuffer(vbd);
224 		vb->Populate(verts);
225 
226 		return vb;
227 	}
228 
229 } // namespace SceneGraph
230