17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-17 12:09:19 +02:00
Files
maszyna/model/Model3d.h
pawelw 51521a135a Text displays: accept characters above 127, in windows-1250 or utf-8 (#198)
* Text displays: accept characters above 127, in windows-1250 or utf-8

A text display (sub-model of type "text") looks up the sub-model of each character
in a 256 entry table, and indexed it with a plain char. With char being signed, any
character above 127 gave a negative index: reading the text went outside the table,
and so did filling the table if a character sub-model had such a name. In effect the
displays could only show ASCII, and a text with national characters could crash.

The lookup is now keyed by unicode code point. Both the displayed text and the names
of the character sub-models are decoded as utf-8 if they are valid utf-8, and as
windows-1250 otherwise, so legacy (ansi) sceneries and models work next to utf-8
ones, and the two can be mixed. A short ansi text can be valid utf-8 by accident
(two upper case accented letters in a row will do); when both readings are possible
the one which finds more of its characters in the model is used.

The rest is unchanged: the first character of the text still isn't displayed, the
characters are still drawn in the translucent pass only. A missing text pointer no
longer gets dereferenced.

* Text displays: prepare the characters once for each text

The sequence of character sub-models was cached for the last text only, so
displays of several instances drawn in turn decoded their texts again on
every draw. It is now kept for each text the display shows; the texts of the
instances don't change, so a draw costs a single lookup.

Also the comments added in the renderers are now in English.

* Text displays: move the text decoding to utilities

utf8_to_utf32() and win1250_to_utf32(), with the windows-1250 table, now sit
next to win1250_to_utf8(). The existing function maps only the Polish letters
and passes other bytes above 127 through, so it can't serve the displays.

* Text displays: address the SonarCloud findings

std::array instead of the C-style arrays, the utf-8 sequence length worked
out in a function of its own instead of nested conditional operators,
std::byte for the bit operations, std::ranges::count_if and contains().
The character loop of the renderers moved to Render_Alpha_text(), which
takes the nesting back to the limit, and the static pasText is accessed
through the class.
2026-10-03 12:05:32 +02:00

318 lines
14 KiB
C++

/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "utilities/Classes.h"
#include "utilities/Float3d.h"
#include "rendering/geometrybank.h"
#include "model/material.h"
#include "gl/query.h"
#define EU07_USE_GEOMETRYINDEXING
// Ra: specjalne typy submodeli, poza tym GL_TRIANGLES itp.
const int TP_ROTATOR = 256;
const int TP_FREESPOTLIGHT = 257;
const int TP_STARS = 258;
const int TP_TEXT = 259;
enum class TAnimType // rodzaj animacji
{
at_None, // brak
at_Rotate, // obrót względem wektora o kąt
at_RotateXYZ, // obrót względem osi o kąty
at_Translate, // przesunięcie
at_SecondsJump, // sekundy z przeskokiem
at_MinutesJump, // minuty z przeskokiem
at_HoursJump, // godziny z przeskokiem 12h/360°
at_Hours24Jump, // godziny z przeskokiem 24h/360°
at_Seconds, // sekundy płynnie
at_Minutes, // minuty płynnie
at_Hours, // godziny płynnie 12h/360°
at_Hours24, // godziny płynnie 24h/360°
at_Billboard, // obrót w pionie do kamery
at_Wind, // ruch pod wpływem wiatru
at_Sky, // animacja nieba
at_Digital, // dziesięciocyfrowy licznik mechaniczny (z cylindrami)
at_DigiClk, // zegar cyfrowy jako licznik na dziesięciościanach
at_Undefined // animacja chwilowo nieokreślona
};
namespace scene {
class shape_node;
}
class TModel3d;
using nameoffset_sequence = std::vector<std::pair<std::string, glm::vec3>>;
class TSubModel
{ // klasa submodelu - pojedyncza siatka, punkt świetlny albo grupa punktów
//m7todo: zrobić normalną serializację
friend opengl_renderer;
friend opengl33_renderer;
friend TModel3d; // temporary workaround. TODO: clean up class content/hierarchy
friend TDynamicObject; // temporary etc
friend scene::shape_node; // temporary etc
public:
enum normalization {
none = 0,
rescale,
normalize
};
struct geometry_data {
gfx::geometry_handle handle;
int vertex_offset;
int vertex_count;
int index_offset;
int index_count;
};
public:
int iNext{ 0 };
int iChild{ 0 };
int eType{ TP_ROTATOR }; // Ra: modele binarne dają więcej możliwości niż mesh złożony z trójkątów
int iName{ -1 }; // numer łańcucha z nazwą submodelu, albo -1 gdy anonimowy
public: // chwilowo
TAnimType b_Anim{ TAnimType::at_None };
bool HasAnyVertexUserData() const;
public:
uint32_t iFlags{ 0x0200 }; // bit 9=1: submodel został utworzony a nie ustawiony na wczytany plik
// flagi informacyjne:
// bit 0: =1 faza rysowania zależy od wymiennej tekstury 0
// bit 1: =1 faza rysowania zależy od wymiennej tekstury 1
// bit 2: =1 faza rysowania zależy od wymiennej tekstury 2
// bit 3: =1 faza rysowania zależy od wymiennej tekstury 3
// bit 4: =1 rysowany w fazie nieprzezroczystych (stała tekstura albo brak)
// bit 5: =1 rysowany w fazie przezroczystych (stała tekstura)
// bit 7: =1 ta sama tekstura, co poprzedni albo nadrzędny
// bit 8: =1 wierzchołki wyświetlane z indeksów
// bit 9: =1 wczytano z pliku tekstowego (jest właścicielem tablic)
// bit 13: =1 wystarczy przesunięcie zamiast mnożenia macierzy (trzy jedynki)
// bit 14: =1 wymagane przechowanie macierzy (animacje)
// bit 15: =1 wymagane przechowanie macierzy (transform niejedynkowy)
union
{ // transform, nie każdy submodel musi mieć
float4x4 *fMatrix = nullptr; // pojedyncza precyzja wystarcza
int iMatrix; // w pliku binarnym jest numer matrycy
};
float transformscalestack { 1.0f }; // tolerancescale used in calculate_indices for whole matrix chain
int iTexture { 0 }; // numer nazwy tekstury, -1 wymienna, 0 brak
float fLight { -1.0f }; // próg jasności światła do zadziałania selfillum
glm::vec4
f4Ambient { 1.0f,1.0f,1.0f,1.0f },
f4Diffuse { 1.0f,1.0f,1.0f,1.0f },
f4Specular { 0.0f,0.0f,0.0f,1.0f },
f4Emision { 1.0f,1.0f,1.0f,1.0f };
glm::vec3 DiffuseOverride { -1.f };
normalization m_normalizenormals { normalization::none }; // indicates vectors need to be normalized due to scaling etc
float diffuseMultiplier {1.0};
float fWireSize { 0.0f }; // nie używane, ale wczytywane
float fSquareMaxDist { 10000.0f * 10000.0f };
float fSquareMinDist { 0.0f };
// McZapkie-050702: parametry dla swiatla:
float fNearAttenStart { 40.0f };
float fNearAttenEnd { 80.0f };
bool bUseNearAtten { false }; // te 3 zmienne okreslaja rysowanie aureoli wokol zrodla swiatla
int iFarAttenDecay { 0 }; // ta zmienna okresla typ zaniku natezenia swiatla (0:brak, 1,2: potega 1/R)
float fFarDecayRadius { 100.0f }; // normalizacja j.w.
float fCosFalloffAngle { 0.5f }; // cosinus kąta stożka pod którym widać światło
float fCosHotspotAngle { 0.3f }; // cosinus kąta stożka pod którym widać aureolę i zwiększone natężenie światła
float fCosViewAngle { 0.0f }; // cos kata pod jakim sie teraz patrzy
bool m_rotation_init_done = false;
public:
TSubModel *Next { nullptr };
TSubModel *Child { nullptr };
public:
material_handle m_material { null_handle }; // numer tekstury, -1 wymienna, 0 brak
// index of the top level branch of the model this submodel belongs to. submodels sharing a branch
// also share the texture variant picked for them, so a multi-mesh load chunk stays consistent
int m_variantgroup { 0 };
bool bWire { false }; // nie używane, ale wczytywane
float Opacity { 1.0f };
float f_Angle { 0.0f };
float3 v_RotateAxis { 0.0f, 0.0f, 0.0f };
float3 v_Angles { 0.0f, 0.0f, 0.0f };
public: // chwilowo
float3 v_TransVector { 0.0f, 0.0f, 0.0f };
geometry_data m_geometry { /*this,*/ { 0, 0 }, 0, 0, 0, 0 };
gfx::vertex_array Vertices;
gfx::userdata_array Userdata;
gfx::index_array Indices;
float m_boundingradius { 0 };
std::uintptr_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację
TAnimType b_aAnim{ TAnimType::at_None }; // kody animacji oddzielnie, bo zerowane
std::shared_ptr<float4x4> mAnimMatrix; // macierz do animacji kwaternionowych
// text display (TP_TEXT) data, filled on first use
std::unordered_map<char32_t, TSubModel *> m_letters; // sub-models of the available characters, by unicode code point
bool m_lettersready { false }; // whether the character lookup was built yet
std::unordered_map<std::string, std::vector<TSubModel *>> m_textlayouts; // sub-models of consecutive characters, for each text shown
TSubModel *Parent{ nullptr }; // nadrzędny, np. do wymnażania macierzy
int iVisible { 1 }; // roboczy stan widoczności
float fVisible { 1.f }; // visibility level
std::string m_materialname; // robocza nazwa tekstury do zapisania w pliku binarnym
std::string pName; // robocza nazwa
public:
int SeekFaceNormal( std::vector<unsigned int> const &Masks, int const Startface, unsigned int const Mask, glm::vec3 const &Position, gfx::vertex_array const &Vertices );
void RaAnimation(TAnimType a);
void RaAnimation(glm::mat4 &m, TAnimType a);
// returns true if the submodel is a smoke emitter attachment point, false otherwise
bool is_emitter() const;
public:
static size_t iInstance; // identyfikator egzemplarza, który aktualnie renderuje model
// seed of the currently rendered model instance, combined with m_variantgroup it picks
// texture variants for materials which declare them. 0 for instances without own seed
static std::uint32_t iVariantSeed;
static material_handle const *ReplacableSkinId;
static int iAlpha; // maska bitowa dla danego przebiegu
static float fSquareDist;
static TModel3d *pRoot;
static std::string *pasText; // tekst dla wyświetlacza (!!!! do przemyślenia)
TSubModel() = default;
~TSubModel();
std::pair<int, int> Load(cParser &Parser, /*TModel3d *Model, int Pos,*/ bool dynamic);
void ChildAdd(TSubModel *SubModel);
void NextAdd(TSubModel *SubModel);
TSubModel * NextGet() { return Next; };
TSubModel * ChildGet() { return Child; };
int count_siblings();
int count_children();
// locates submodel mapped with replacable -4
std::tuple<TSubModel *, bool> find_replacable4();
// locates particle emitter submodels and adds them to provided list
void find_smoke_sources( nameoffset_sequence &Sourcelist ) const;
#ifndef EU07_USE_GEOMETRYINDEXING
int TriangleAdd(TModel3d *m, material_handle tex, int tri);
#endif
void SetRotate(float3 vNewRotateAxis, float fNewAngle);
void SetRotateXYZ(glm::vec3 vNewAngles);
void SetRotateXYZ(float3 vNewAngles);
void SetTranslate(glm::vec3 vNewTransVector);
void SetTranslate(float3 vNewTransVector);
void SetRotateIK1(float3 vNewAngles);
TSubModel * GetFromName( std::string const &search, bool i = true );
// text display (TP_TEXT): returns sub-models of the characters to draw for provided text, in drawing order
std::vector<TSubModel *> const & text_letters( std::string const &Text );
inline float4x4 * GetMatrix() { return fMatrix; };
inline float4x4 const * GetMatrix() const { return fMatrix; };
// returns offset vector from root
glm::vec3 offset( float const Geometrytestoffsetthreshold = 0.f ) const;
inline void Hide() { iVisible = 0; };
void create_geometry( std::size_t &Indexoffset, std::size_t &Vertexoffset, gfx::geometrybank_handle const &Bank );
uint32_t FlagsCheck();
void WillBeAnimated() {
iFlags |= 0x4000; };
void InitialRotate(bool doit);
void BinInit(TSubModel *s, float4x4 *m, std::vector<std::string> *t, std::vector<std::string> *n, bool dynamic);
static void ReplacableSet(material_handle const *r, int a) {
ReplacableSkinId = r;
iAlpha = a; };
// returns id of texture variant to use for provided submodel of the currently rendered instance
static std::uint32_t VariantId(TSubModel const *Submodel);
void Name_Material( std::string const &Name );
void Name( std::string const &Name );
// Ra: funkcje do budowania terenu z E3D
uint32_t Flags() const { return iFlags; };
void UnFlagNext() { iFlags &= 0x00FFFFFF; };
void ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular );
// sets rgb components of diffuse color override to specified value
void SetDiffuseOverride( glm::vec3 const &Color, bool const Includechildren = false, bool const Includesiblings = false );
// gets rgb components of any freespot diffuse color (searches also in children)
std::optional<glm::vec3> GetDiffuse( float Includesiblings = false );
// sets visibility level (alpha component) to specified value
void SetVisibilityLevel( float const Level, bool const Includechildren = false, bool const Includesiblings = false );
// sets light level (alpha component of illumination color) to specified value
void SetLightLevel( glm::vec4 const &Level, bool const Includechildren = false, bool const Includesiblings = false );
// sets activation threshold of self-illumination to specitied value
void SetSelfIllum( float const Threshold, bool const Includechildren = false, bool const Includesiblings = false );
inline float3 Translation1Get() {
return fMatrix ? *fMatrix->TranslationGet() + v_TransVector : v_TransVector; }
inline float3 Translation2Get() {
return *fMatrix->TranslationGet() + Child->Translation1Get(); }
material_handle GetMaterial() const {
return m_material; }
void ParentMatrix(float4x4 *m) const;
void ReplaceMatrix(const glm::mat4 &mat);
void ReplaceMaterial(const std::string &name);
float MaxY( float4x4 const &m );
std::shared_ptr<std::vector<glm::vec2>> screen_touch_list; // for python screen touching
std::optional<gl::query> occlusion_query;
glm::mat4 future_transform;
void deserialize(std::istream&);
void serialize(std::ostream&,
std::vector<TSubModel*>&,
std::vector<std::string>&,
std::vector<std::string>&,
std::vector<float4x4>&);
void serialize_geometry( std::ostream &Output, bool const Packed, bool const Indexed, bool const UserData ) const;
int index_size() const;
void serialize_indices( std::ostream &Output, int const Size ) const;
// places contained geometry in provided ground node
};
class TModel3d
{
friend opengl_renderer;
friend opengl33_renderer;
public:
TSubModel *Root { nullptr }; // drzewo submodeli
uint32_t iFlags { 0 }; // Ra: czy submodele mają przezroczyste tekstury
public: // Ra: tymczasowo
gfx::geometrybank_handle m_geometrybank;
int m_indexcount { 0 };
int m_vertexcount { 0 }; // ilość wierzchołków
private:
std::vector<std::string> Textures; // nazwy tekstur
std::vector<std::string> Names; // nazwy submodeli
std::vector<float4x4> Matrices; // submodel matrices
int iSubModelsCount { 0 }; // Ra: używane do tworzenia binarnych
std::string asBinary; // nazwa pod którą zapisać model binarny
std::string m_filename;
nameoffset_sequence m_smokesources; // list of particle sources defined in the model
public:
TModel3d() = default;
~TModel3d();
float bounding_radius() const {
return Root ? Root->m_boundingradius : 0.f; }
inline TSubModel * GetSMRoot() { return Root; };
TSubModel * GetFromName(std::string const &Name) const;
TSubModel * AddToNamed(const char *Name, TSubModel *SubModel);
nameoffset_sequence const & find_smoke_sources();
void AddTo(TSubModel *tmp, TSubModel *SubModel);
void LoadFromTextFile(std::string const &FileName, bool dynamic);
void LoadFromBinFile(std::string const &FileName, bool dynamic);
bool LoadFromFile(std::string const &FileName, bool dynamic);
TSubModel *AppendChildFromGeometry(const std::string &name, const std::string &parent, const gfx::vertex_array &vertices, const gfx::index_array &indices);
void SaveToBinFile(std::string const &FileName);
uint32_t Flags() const { return iFlags; };
void Init();
// assigns each top level branch of the model, and everything below it, its own variant group id
void assign_variant_groups();
std::string NameGet() const { return m_filename; };
nameoffset_sequence const & smoke_sources() const {
return m_smokesources; }
int TerrainCount() const;
TSubModel * TerrainSquare(int n);
void deserialize(std::istream &s, size_t size, bool dynamic);
};
//---------------------------------------------------------------------------