1 #include "lc_global.h"
2 #include "lc_scene.h"
3 #include "lc_context.h"
4 #include "pieceinf.h"
5 #include "lc_texture.h"
6 #include "lc_library.h"
7 #include "lc_application.h"
8 #include "object.h"
9
lcScene()10 lcScene::lcScene()
11 : mRenderMeshes(0, 1024), mOpaqueMeshes(0, 1024), mTranslucentMeshes(0, 1024), mInterfaceObjects(0, 1024)
12 {
13 mActiveSubmodelInstance = nullptr;
14 mDrawInterface = false;
15 mShadingMode = lcShadingMode::DefaultLights;
16 mAllowLOD = true;
17 mMeshLODDistance = 250.0f;
18 mHasFadedParts = false;
19 mPreTranslucentCallback = nullptr;
20 }
21
Begin(const lcMatrix44 & ViewMatrix)22 void lcScene::Begin(const lcMatrix44& ViewMatrix)
23 {
24 mViewMatrix = ViewMatrix;
25 mActiveSubmodelInstance = nullptr;
26 mPreTranslucentCallback = nullptr;
27 mRenderMeshes.RemoveAll();
28 mOpaqueMeshes.RemoveAll();
29 mTranslucentMeshes.RemoveAll();
30 mInterfaceObjects.RemoveAll();
31
32 const lcPreferences& Preferences = lcGetPreferences();
33 mHighlightColor = lcVector4FromColor(Preferences.mHighlightNewPartsColor);
34 mFadeColor = lcVector4FromColor(Preferences.mFadeStepsColor);
35 mHasFadedParts = false;
36 mTranslucentFade = mFadeColor.w != 1.0f;
37 }
38
End()39 void lcScene::End()
40 {
41 const auto OpaqueMeshCompare = [this](int Index1, int Index2)
42 {
43 const lcMesh* Mesh1 = mRenderMeshes[Index1].Mesh;
44 const lcMesh* Mesh2 = mRenderMeshes[Index2].Mesh;
45
46 const int Texture1 = Mesh1->mFlags & lcMeshFlag::HasTexture;
47 const int Texture2 = Mesh2->mFlags & lcMeshFlag::HasTexture;
48
49 if (Texture1 == Texture2)
50 return Mesh1 < Mesh2;
51
52 return Texture1 ? false : true;
53 };
54
55 std::sort(mOpaqueMeshes.begin(), mOpaqueMeshes.end(), OpaqueMeshCompare);
56
57 auto TranslucentMeshCompare = [](const lcTranslucentMeshInstance& Mesh1, const lcTranslucentMeshInstance& Mesh2)
58 {
59 return Mesh1.Distance > Mesh2.Distance;
60 };
61
62 std::sort(mTranslucentMeshes.begin(), mTranslucentMeshes.end(), TranslucentMeshCompare);
63 }
64
AddMesh(lcMesh * Mesh,const lcMatrix44 & WorldMatrix,int ColorIndex,lcRenderMeshState State)65 void lcScene::AddMesh(lcMesh* Mesh, const lcMatrix44& WorldMatrix, int ColorIndex, lcRenderMeshState State)
66 {
67 lcRenderMesh& RenderMesh = mRenderMeshes.Add();
68
69 RenderMesh.WorldMatrix = WorldMatrix;
70 RenderMesh.Mesh = Mesh;
71 RenderMesh.ColorIndex = ColorIndex;
72 RenderMesh.State = State;
73 const float Distance = fabsf(lcMul31(WorldMatrix[3], mViewMatrix).z) - mMeshLODDistance;
74 RenderMesh.LodIndex = mAllowLOD ? RenderMesh.Mesh->GetLodIndex(Distance) : LC_MESH_LOD_HIGH;
75
76 const bool ForceTranslucent = (mTranslucentFade && State == lcRenderMeshState::Faded);
77 const bool Translucent = lcIsColorTranslucent(ColorIndex) || ForceTranslucent;
78 const lcMeshFlags Flags = Mesh->mFlags;
79 mHasFadedParts |= State == lcRenderMeshState::Faded;
80
81 if ((Flags & (lcMeshFlag::HasSolid | lcMeshFlag::HasLines)) || ((Flags & lcMeshFlag::HasDefault) && !Translucent))
82 mOpaqueMeshes.Add(mRenderMeshes.GetSize() - 1);
83
84 if ((Flags & lcMeshFlag::HasTranslucent) || ((Flags & lcMeshFlag::HasDefault) && Translucent))
85 {
86 const lcMeshLod& Lod = Mesh->mLods[RenderMesh.LodIndex];
87
88 for (int SectionIdx = 0; SectionIdx < Lod.NumSections; SectionIdx++)
89 {
90 const lcMeshSection* Section = &Lod.Sections[SectionIdx];
91
92 if ((Section->PrimitiveType & (LC_MESH_TRIANGLES | LC_MESH_TEXTURED_TRIANGLES)) == 0)
93 continue;
94
95 int SectionColorIndex = Section->ColorIndex;
96
97 if (SectionColorIndex == gDefaultColor)
98 SectionColorIndex = RenderMesh.ColorIndex;
99
100 if (!lcIsColorTranslucent(SectionColorIndex) && !ForceTranslucent)
101 continue;
102
103 const lcVector3 Center = (Section->BoundingBox.Min + Section->BoundingBox.Max) / 2;
104 const float InstanceDistance = fabsf(lcMul31(lcMul31(Center, WorldMatrix), mViewMatrix).z);
105
106 lcTranslucentMeshInstance& Instance = mTranslucentMeshes.Add();
107 Instance.Section = Section;
108 Instance.Distance = InstanceDistance;
109 Instance.RenderMeshIndex = mRenderMeshes.GetSize() - 1;
110 }
111 }
112 }
113
DrawDebugNormals(lcContext * Context,const lcMesh * Mesh) const114 void lcScene::DrawDebugNormals(lcContext* Context, const lcMesh* Mesh) const
115 {
116 const lcVertex* const VertexBuffer = Mesh->GetVertexData();
117 const lcVertexTextured* const TexturedVertexBuffer = Mesh->GetTexturedVertexData();
118 lcVector3* const Vertices = (lcVector3*)malloc((Mesh->mNumVertices + Mesh->mNumTexturedVertices) * 2 * sizeof(lcVector3));
119
120 for (int VertexIdx = 0; VertexIdx < Mesh->mNumVertices; VertexIdx++)
121 {
122 Vertices[VertexIdx * 2] = VertexBuffer[VertexIdx].Position;
123 Vertices[VertexIdx * 2 + 1] = VertexBuffer[VertexIdx].Position + lcUnpackNormal(VertexBuffer[VertexIdx].Normal);
124 }
125
126 for (int VertexIdx = 0; VertexIdx < Mesh->mNumTexturedVertices; VertexIdx++)
127 {
128 Vertices[(Mesh->mNumVertices + VertexIdx) * 2] = TexturedVertexBuffer[VertexIdx].Position;
129 Vertices[(Mesh->mNumVertices + VertexIdx) * 2 + 1] = TexturedVertexBuffer[VertexIdx].Position + lcUnpackNormal(TexturedVertexBuffer[VertexIdx].Normal);
130 }
131
132 Context->SetVertexBufferPointer(Vertices);
133 Context->SetVertexFormatPosition(3);
134 Context->DrawPrimitives(GL_LINES, 0, (Mesh->mNumVertices + Mesh->mNumTexturedVertices) * 2);
135 free(Vertices);
136 }
137
DrawOpaqueMeshes(lcContext * Context,bool DrawLit,int PrimitiveTypes,bool DrawFaded,bool DrawNonFaded) const138 void lcScene::DrawOpaqueMeshes(lcContext* Context, bool DrawLit, int PrimitiveTypes, bool DrawFaded, bool DrawNonFaded) const
139 {
140 if (mOpaqueMeshes.IsEmpty())
141 return;
142
143 lcMaterialType FlatMaterial, TexturedMaterial;
144
145 if (DrawLit)
146 {
147 FlatMaterial = lcMaterialType::FakeLitColor;
148 TexturedMaterial = lcMaterialType::FakeLitTextureDecal;
149 }
150 else
151 {
152 FlatMaterial = lcMaterialType::UnlitColor;
153 TexturedMaterial = lcMaterialType::UnlitTextureDecal;
154 }
155
156 Context->SetPolygonOffset(lcPolygonOffset::Opaque);
157
158 for (const int MeshIndex : mOpaqueMeshes)
159 {
160 const lcRenderMesh& RenderMesh = mRenderMeshes[MeshIndex];
161 const lcMesh* Mesh = RenderMesh.Mesh;
162 const int LodIndex = RenderMesh.LodIndex;
163
164 if (!DrawFaded && RenderMesh.State == lcRenderMeshState::Faded)
165 continue;
166
167 if (!DrawNonFaded && RenderMesh.State != lcRenderMeshState::Faded)
168 continue;
169
170 Context->BindMesh(Mesh);
171 Context->SetWorldMatrix(RenderMesh.WorldMatrix);
172
173 for (int SectionIdx = 0; SectionIdx < Mesh->mLods[LodIndex].NumSections; SectionIdx++)
174 {
175 const lcMeshSection* const Section = &Mesh->mLods[LodIndex].Sections[SectionIdx];
176
177 if ((Section->PrimitiveType & PrimitiveTypes) == 0)
178 continue;
179
180 int ColorIndex = Section->ColorIndex;
181
182 if (Section->PrimitiveType & (LC_MESH_TRIANGLES | LC_MESH_TEXTURED_TRIANGLES))
183 {
184 if (ColorIndex == gDefaultColor)
185 ColorIndex = RenderMesh.ColorIndex;
186
187 if (lcIsColorTranslucent(ColorIndex))
188 continue;
189
190 switch (RenderMesh.State)
191 {
192 case lcRenderMeshState::Default:
193 case lcRenderMeshState::Highlighted:
194 Context->SetColorIndex(ColorIndex);
195 break;
196
197 case lcRenderMeshState::Selected:
198 Context->SetColorIndexTinted(ColorIndex, LC_COLOR_SELECTED, 0.5f);
199 break;
200
201 case lcRenderMeshState::Focused:
202 Context->SetColorIndexTinted(ColorIndex, LC_COLOR_FOCUSED, 0.5f);
203 break;
204
205 case lcRenderMeshState::Faded:
206 if (mTranslucentFade)
207 continue;
208 Context->SetColorIndexTinted(ColorIndex, mFadeColor);
209 break;
210 }
211 }
212 else if (Section->PrimitiveType & (LC_MESH_LINES | LC_MESH_CONDITIONAL_LINES))
213 {
214 switch (RenderMesh.State)
215 {
216 case lcRenderMeshState::Default:
217 if (mShadingMode != lcShadingMode::Wireframe)
218 {
219 if (ColorIndex != gEdgeColor)
220 Context->SetColorIndex(ColorIndex);
221 else
222 Context->SetEdgeColorIndex(RenderMesh.ColorIndex);
223 }
224 else
225 {
226 if (ColorIndex == gEdgeColor)
227 ColorIndex = RenderMesh.ColorIndex;
228
229 Context->SetColorIndex(ColorIndex);
230 }
231 break;
232
233 case lcRenderMeshState::Selected:
234 Context->SetInterfaceColor(LC_COLOR_SELECTED);
235 break;
236
237 case lcRenderMeshState::Focused:
238 Context->SetInterfaceColor(LC_COLOR_FOCUSED);
239 break;
240
241 case lcRenderMeshState::Highlighted:
242 Context->SetColor(mHighlightColor);
243 break;
244
245 case lcRenderMeshState::Faded:
246 Context->SetEdgeColorIndexTinted(ColorIndex, mFadeColor);
247 break;
248 }
249
250 if (Section->PrimitiveType == LC_MESH_CONDITIONAL_LINES)
251 {
252 int VertexBufferOffset = Mesh->mVertexCacheOffset != -1 ? Mesh->mVertexCacheOffset : 0;
253 VertexBufferOffset += Mesh->mNumVertices * sizeof(lcVertex) + Mesh->mNumTexturedVertices * sizeof(lcVertexTextured);
254 const int IndexBufferOffset = Mesh->mIndexCacheOffset != -1 ? Mesh->mIndexCacheOffset : 0;
255
256 Context->SetMaterial(lcMaterialType::UnlitColorConditional);
257 Context->SetVertexFormatConditional(VertexBufferOffset);
258
259 Context->DrawIndexedPrimitives(GL_LINES, Section->NumIndices, Mesh->mIndexType, IndexBufferOffset + Section->IndexOffset);
260
261 continue;
262 }
263 }
264
265 lcTexture* const Texture = Section->Texture;
266 int VertexBufferOffset = Mesh->mVertexCacheOffset != -1 ? Mesh->mVertexCacheOffset : 0;
267 const int IndexBufferOffset = Mesh->mIndexCacheOffset != -1 ? Mesh->mIndexCacheOffset : 0;
268
269 if (!Texture)
270 {
271 Context->SetMaterial(FlatMaterial);
272 Context->SetVertexFormat(VertexBufferOffset, 3, 1, 0, 0, DrawLit);
273 }
274 else
275 {
276 if (Texture->NeedsUpload())
277 Texture->Upload(Context);
278 Context->SetMaterial(TexturedMaterial);
279 VertexBufferOffset += Mesh->mNumVertices * sizeof(lcVertex);
280 Context->SetVertexFormat(VertexBufferOffset, 3, 1, 2, 0, DrawLit);
281 Context->BindTexture2D(Texture->mTexture);
282 }
283
284 const GLenum DrawPrimitiveType = Section->PrimitiveType & (LC_MESH_TRIANGLES | LC_MESH_TEXTURED_TRIANGLES) ? GL_TRIANGLES : GL_LINES;
285 Context->DrawIndexedPrimitives(DrawPrimitiveType, Section->NumIndices, Mesh->mIndexType, IndexBufferOffset + Section->IndexOffset);
286 }
287
288 #ifdef LC_DEBUG_NORMALS
289 DrawDebugNormals(Context, Mesh);
290 #endif
291 }
292
293 Context->BindTexture2D(0);
294 Context->SetPolygonOffset(lcPolygonOffset::None);
295 }
296
DrawTranslucentMeshes(lcContext * Context,bool DrawLit,bool DrawFadePrepass,bool DrawFaded,bool DrawNonFaded) const297 void lcScene::DrawTranslucentMeshes(lcContext* Context, bool DrawLit, bool DrawFadePrepass, bool DrawFaded, bool DrawNonFaded) const
298 {
299 if (mTranslucentMeshes.IsEmpty())
300 return;
301
302 lcMaterialType FlatMaterial, TexturedMaterial;
303
304 if (DrawLit)
305 {
306 FlatMaterial = lcMaterialType::FakeLitColor;
307 TexturedMaterial = lcMaterialType::FakeLitTextureDecal;
308 }
309 else
310 {
311 FlatMaterial = lcMaterialType::UnlitColor;
312 TexturedMaterial = lcMaterialType::UnlitTextureDecal;
313 }
314
315 if (!DrawFadePrepass)
316 {
317 glEnable(GL_BLEND);
318 Context->SetDepthWrite(false);
319 }
320 else
321 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
322
323 Context->SetPolygonOffset(lcPolygonOffset::Translucent);
324
325 for (const lcTranslucentMeshInstance& MeshInstance : mTranslucentMeshes)
326 {
327 const lcRenderMesh& RenderMesh = mRenderMeshes[MeshInstance.RenderMeshIndex];
328 const lcMesh* Mesh = RenderMesh.Mesh;
329
330 if (!DrawFaded && RenderMesh.State == lcRenderMeshState::Faded)
331 continue;
332
333 if (!DrawNonFaded && RenderMesh.State != lcRenderMeshState::Faded)
334 continue;
335
336 Context->BindMesh(Mesh);
337 Context->SetWorldMatrix(RenderMesh.WorldMatrix);
338
339 const lcMeshSection* Section = MeshInstance.Section;
340
341 int ColorIndex = Section->ColorIndex;
342
343 if (ColorIndex == gDefaultColor)
344 ColorIndex = RenderMesh.ColorIndex;
345
346 if (DrawFadePrepass && lcIsColorTranslucent(ColorIndex))
347 continue;
348
349 switch (RenderMesh.State)
350 {
351 case lcRenderMeshState::Default:
352 case lcRenderMeshState::Highlighted:
353 Context->SetColorIndex(ColorIndex);
354 break;
355
356 case lcRenderMeshState::Selected:
357 Context->SetColorIndexTinted(ColorIndex, LC_COLOR_SELECTED, 0.5f);
358 break;
359
360 case lcRenderMeshState::Focused:
361 Context->SetColorIndexTinted(ColorIndex, LC_COLOR_FOCUSED, 0.5f);
362 break;
363
364 case lcRenderMeshState::Faded:
365 Context->SetColorIndexTinted(ColorIndex, mFadeColor);
366 break;
367 }
368
369 lcTexture* const Texture = Section->Texture;
370 int VertexBufferOffset = Mesh->mVertexCacheOffset != -1 ? Mesh->mVertexCacheOffset : 0;
371 const int IndexBufferOffset = Mesh->mIndexCacheOffset != -1 ? Mesh->mIndexCacheOffset : 0;
372
373 if (!Texture)
374 {
375 Context->SetMaterial(FlatMaterial);
376 Context->SetVertexFormat(VertexBufferOffset, 3, 1, 0, 0, DrawLit);
377 }
378 else
379 {
380 if (Texture->NeedsUpload())
381 Texture->Upload(Context);
382 Context->SetMaterial(TexturedMaterial);
383 VertexBufferOffset += Mesh->mNumVertices * sizeof(lcVertex);
384 Context->SetVertexFormat(VertexBufferOffset, 3, 1, 2, 0, DrawLit);
385 Context->BindTexture2D(Texture->mTexture);
386 }
387
388 const GLenum DrawPrimitiveType = Section->PrimitiveType & (LC_MESH_TRIANGLES | LC_MESH_TEXTURED_TRIANGLES) ? GL_TRIANGLES : GL_LINES;
389 Context->DrawIndexedPrimitives(DrawPrimitiveType, Section->NumIndices, Mesh->mIndexType, IndexBufferOffset + Section->IndexOffset);
390
391 #ifdef LC_DEBUG_NORMALS
392 DrawDebugNormals(Context, Mesh);
393 #endif
394 }
395
396 Context->BindTexture2D(0);
397 Context->SetPolygonOffset(lcPolygonOffset::None);
398
399 if (!DrawFadePrepass)
400 {
401 Context->SetDepthWrite(true);
402 glDisable(GL_BLEND);
403 }
404 else
405 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
406 }
407
Draw(lcContext * Context) const408 void lcScene::Draw(lcContext* Context) const
409 {
410 // TODO: find a better place for these updates
411 lcGetPiecesLibrary()->UpdateBuffers(Context);
412
413 Context->SetViewMatrix(mViewMatrix);
414
415 const lcPreferences& Preferences = lcGetPreferences();
416 const bool DrawLines = Preferences.mDrawEdgeLines && Preferences.mLineWidth > 0.0f;
417 const bool DrawConditional = Preferences.mDrawConditionalLines && Preferences.mLineWidth > 0.0f;
418
419 // lcShadingMode ShadingMode = Preferences.mShadingMode;
420 // if (ShadingMode == lcShadingMode::Wireframe && !mAllowWireframe)
421 // ShadingMode = lcShadingMode::Flat;
422
423 if (mShadingMode == lcShadingMode::Wireframe)
424 {
425 DrawOpaqueMeshes(Context, false, LC_MESH_LINES, true, true);
426
427 if (DrawConditional)
428 DrawOpaqueMeshes(Context, false, LC_MESH_CONDITIONAL_LINES, true, true);
429
430 if (mPreTranslucentCallback)
431 mPreTranslucentCallback();
432 }
433 else if (mShadingMode == lcShadingMode::Flat)
434 {
435 const int SolidPrimitiveTypes = LC_MESH_TRIANGLES | LC_MESH_TEXTURED_TRIANGLES | (DrawLines ? LC_MESH_LINES : 0);
436
437 if (mTranslucentFade && mHasFadedParts)
438 {
439 DrawOpaqueMeshes(Context, false, SolidPrimitiveTypes, false, true);
440
441 if (mPreTranslucentCallback)
442 mPreTranslucentCallback();
443
444 DrawTranslucentMeshes(Context, false, true, true, false);
445
446 if (DrawLines)
447 DrawOpaqueMeshes(Context, false, LC_MESH_LINES, true, false);
448
449 if (DrawConditional)
450 DrawOpaqueMeshes(Context, false, LC_MESH_CONDITIONAL_LINES, true, false);
451
452 DrawTranslucentMeshes(Context, false, false, true, true);
453 }
454 else
455 {
456 DrawOpaqueMeshes(Context, false, SolidPrimitiveTypes, true, true);
457
458 if (DrawConditional)
459 DrawOpaqueMeshes(Context, false, LC_MESH_CONDITIONAL_LINES, true, true);
460
461 if (mPreTranslucentCallback)
462 mPreTranslucentCallback();
463
464 DrawTranslucentMeshes(Context, false, false, true, true);
465 }
466 }
467 else
468 {
469 if (mTranslucentFade && mHasFadedParts)
470 {
471 DrawOpaqueMeshes(Context, true, LC_MESH_TRIANGLES | LC_MESH_TEXTURED_TRIANGLES, false, true);
472
473 if (DrawLines)
474 DrawOpaqueMeshes(Context, false, LC_MESH_LINES, false, true);
475
476 if (DrawConditional)
477 DrawOpaqueMeshes(Context, false, LC_MESH_CONDITIONAL_LINES, false, true);
478
479 if (mPreTranslucentCallback)
480 mPreTranslucentCallback();
481
482 DrawTranslucentMeshes(Context, false, true, true, false);
483
484 if (DrawLines)
485 DrawOpaqueMeshes(Context, false, LC_MESH_LINES, true, false);
486
487 if (DrawConditional)
488 DrawOpaqueMeshes(Context, false, LC_MESH_CONDITIONAL_LINES, true, false);
489
490 DrawTranslucentMeshes(Context, true, false, true, true);
491 }
492 else
493 {
494 if (DrawLines)
495 DrawOpaqueMeshes(Context, false, LC_MESH_LINES, true, true);
496
497 if (DrawConditional)
498 DrawOpaqueMeshes(Context, false, LC_MESH_CONDITIONAL_LINES, true, true);
499
500 DrawOpaqueMeshes(Context, true, LC_MESH_TRIANGLES | LC_MESH_TEXTURED_TRIANGLES, true, true);
501
502 if (mPreTranslucentCallback)
503 mPreTranslucentCallback();
504
505 DrawTranslucentMeshes(Context, true, false, true, true);
506 }
507 }
508 }
509
DrawInterfaceObjects(lcContext * Context) const510 void lcScene::DrawInterfaceObjects(lcContext* Context) const
511 {
512 for (const lcObject* Object : mInterfaceObjects)
513 Object->DrawInterface(Context, *this);
514 }
515