From 786f1823f06393150a5f8d35a9f26f177b233625 Mon Sep 17 00:00:00 2001 From: Martin Schweiger Date: Fri, 28 Oct 2022 04:02:33 +0100 Subject: [PATCH] #283: Included flags for force vector display options to Graphics API. Extended GetConfigParam to return force vector display parameters. Implemented force vector display in D3D7Client. Still to do: text labels. --- OVP/D3D7Client/Scene.cpp | 8 ++ OVP/D3D7Client/VObject.cpp | 174 +++++++++++++++++++++++++++++++ OVP/D3D7Client/VObject.h | 29 ++++++ OVP/D3D7Client/VVessel.cpp | 50 +++++++++ OVP/D3D7Client/VVessel.h | 5 + Orbitersdk/include/GraphicsAPI.h | 36 ++++++- Src/Orbiter/Config.cpp | 6 ++ Src/Orbiter/Config.h | 10 -- 8 files changed, 307 insertions(+), 11 deletions(-) diff --git a/OVP/D3D7Client/Scene.cpp b/OVP/D3D7Client/Scene.cpp index 85b5dee3b..9f8c0471e 100644 --- a/OVP/D3D7Client/Scene.cpp +++ b/OVP/D3D7Client/Scene.cpp @@ -539,6 +539,14 @@ void Scene::Render () if (ptex) dev->SetTexture (0, 0); if (!alpha) dev->SetRenderState (D3DRENDERSTATE_ALPHABLENDENABLE, FALSE); + // render object vectors + if (*(DWORD*)gc->GetConfigParam(CFGPRM_FORCEVECTORFLAG) & BF_ENABLE) { + cam->SetFrustumLimits(1.0, 1e30); + for (pv = vobjFirst; pv; pv = pv->next) + pv->vobj->RenderVectors(dev); + cam->SetFrustumLimits(npl, fpl); + } + // render the internal parts of the focus object in a separate render pass if (oapiCameraInternal() && vFocus) { // switch cockpit lights on, external-only lights off diff --git a/OVP/D3D7Client/VObject.cpp b/OVP/D3D7Client/VObject.cpp index a4254f360..dee76d2da 100644 --- a/OVP/D3D7Client/VObject.cpp +++ b/OVP/D3D7Client/VObject.cpp @@ -30,6 +30,7 @@ #include "Camera.h" #include "Texture.h" #include "D3D7Util.h" +#include using namespace oapi; @@ -87,6 +88,11 @@ void vObject::Activate (bool isactive) active = isactive; } +bool veccomp(const vObject::BodyVectorRec& v1, const vObject::BodyVectorRec& v2) +{ + return v1.dist < v2.dist; // sort from furthest to nearest +} + bool vObject::Update () { if (!active) return false; @@ -109,6 +115,9 @@ bool vObject::Update () // update the object's world matrix CheckResolution(); + UpdateRenderVectors(); + std::sort(veclist.begin(), veclist.end(), veccomp); + return true; } @@ -165,3 +174,168 @@ void vObject::RenderSpot (LPDIRECT3DDEVICE7 dev, const VECTOR3 *ofs, float size, dev->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_MODULATE); dev->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE); } + +void vObject::AddVector(const VECTOR3& v, const VECTOR3& orig, double rad, const std::string& label, const VECTOR3& col, float alpha, DWORD lcol, float lsize) +{ + double len = length(v); + if (len < 2.0 * rad) return; // too short to be rendered + + VECTOR3 cam = -cpos; + VECTOR3 vu = v / len; + double dist = length(vu - cam); + + BodyVectorRec rec; + rec.v = v; + rec.orig = orig; + rec.rad = rad; + rec.dist = dist; + rec.col = col; + rec.alpha = alpha; + rec.label = label; + rec.lcol = lcol; + rec.lsize = lsize; + + veclist.push_back(rec); +} + +void vObject::RenderVectors(LPDIRECT3DDEVICE7 dev) +{ + if (veclist.size()) { + float alpha, palpha = -1.0f; + dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TFACTOR); + dev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + dev->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTFG_POINT); + dev->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFN_POINT); + for (auto it = veclist.begin(); it != veclist.end(); it++) { + alpha = (*it).alpha; + if (alpha != palpha) + dev->SetRenderState(D3DRENDERSTATE_TEXTUREFACTOR, D3DRGBA(1, 1, 1, alpha)); + if (DrawVector(dev, (*it).v, (*it).orig, (*it).rad, (*it).col, alpha) && (*it).label.size()) { + double scale3 = ((*it).lsize >= 0 ? (*it).lsize : size); + //scene->Render3DLabel(mul(body->GRot(), ve->v + ve->v.unit() * (scale3 * 0.1)) + body->GPos(), + // ve->label, scale3, ve->lcol); + } + } + dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + dev->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTFG_LINEAR); + dev->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFN_LINEAR); + } +} + +bool vObject::DrawVector(LPDIRECT3DDEVICE7 dev, const VECTOR3& end, const VECTOR3& orig, double rad, const VECTOR3& col, float opac) +{ + static const float EPS = 1e-2f; + static const int nseg = 8; + static const int nVtx = nseg * 5; + static const int nIdx0 = nseg * 6 + nseg * 3; + static const int nIdx1 = nIdx0 + (nseg - 2) * 3; + static D3DMATERIAL7 pmtrl, mtrl = { + {0,0,0,1}, + {0,0,0,1}, + {0.5,0.5,0.5,1}, + {0,0,0,1}, + 10.0 + }; + int i, nIdx; + WORD* Idx; + static D3DVERTEX* Vtx0 = 0, * Vtx = 0; + static WORD* Idx0 = 0, * Idx1 = 0; + if (!Vtx0) { // create vector mesh template (pointing to +y) + int i0, i1; + Vtx0 = new D3DVERTEX[nVtx]; + Vtx = new D3DVERTEX[nVtx]; + Idx0 = new WORD[nIdx0]; + Idx1 = new WORD[nIdx1]; + for (i = 0; i < nseg; i++) { + Vtx0[i + 2 * nseg].x = Vtx0[i + 3 * nseg].x = 2.0f * (Vtx0[i].x = Vtx0[i + nseg].x = Vtx0[i].nx = Vtx0[i + nseg].nx = (float)cos(PI2 * (double)i / (double)nseg)); + Vtx0[i + 2 * nseg].z = Vtx0[i + 3 * nseg].z = 2.0f * (Vtx0[i].z = Vtx0[i + nseg].z = Vtx0[i].nz = Vtx0[i + nseg].nz = (float)sin(PI2 * (double)i / (double)nseg)); + Vtx0[i + 4 * nseg].x = Vtx0[i + 4 * nseg].z = 0.0f; + Vtx0[i].y = 0.0f; Vtx0[i + nseg].y = Vtx0[i + 2 * nseg].y = Vtx0[i + 3 * nseg].y = Vtx0[i + 4 * nseg].y = 1.0f; + Vtx0[i + 3 * nseg].nx = Vtx0[i + 4 * nseg].nx = (float)(0.894 * cos(PI2 * (double)(i + 0.5) / (double)nseg)); + Vtx0[i + 3 * nseg].nz = Vtx0[i + 4 * nseg].nz = (float)(0.894 * sin(PI2 * (double)(i + 0.5) / (double)nseg)); + Vtx0[i + 3 * nseg].ny = Vtx0[i + 4 * nseg].ny = 0.447f; + Vtx0[i].ny = Vtx0[i + nseg].ny = Vtx0[i + 2 * nseg].nx = Vtx0[i + 2 * nseg].nz = 0.0f; + Vtx0[i + 2 * nseg].ny = -1.0f; + } + for (i = i0 = 0, i1 = 6 * (nseg - 1); i < nseg; i++) { // stem + Idx0[i0++] = Idx1[i1++] = (WORD)i; + Idx0[i0++] = Idx1[i1++] = (WORD)(i + nseg); + Idx0[i0++] = Idx1[i1++] = (WORD)((i + 1) % nseg + nseg); + Idx0[i0++] = Idx1[i1++] = (WORD)i; + Idx0[i0++] = Idx1[i1++] = (WORD)((i + 1) % nseg + nseg); + Idx0[i0++] = Idx1[i1++] = (WORD)((i + 1) % nseg); + } + for (i = 0, i1 = nseg * 3; i < nseg - 2; i++) { // tip base + Idx1[i1++] = nseg * 2; + Idx1[i1++] = nseg * 2 + 1 + i; + Idx1[i1++] = nseg * 2 + 2 + i; + } + for (i = i1 = 0, i0 = nseg * 6; i < nseg; i++) { // tip + Idx0[i0++] = Idx1[i1++] = nseg * 3 + i; + Idx0[i0++] = Idx1[i1++] = nseg * 4 + i; + Idx0[i0++] = Idx1[i1++] = 3 * nseg + (i + 1) % nseg; + } + } + + float w = (float)rad; + float h = (float)length(end); + if (h < EPS) return false; + float hb = max(h - 4.0f * w, 0); + DWORD bZbuf; + + memcpy(Vtx, Vtx0, nVtx * sizeof(D3DVERTEX)); + + for (i = 0; i < nseg; i++) { + Vtx[i + 3 * nseg].y = Vtx[i + 2 * nseg].y = Vtx[i + nseg].y = hb; + Vtx[i + 4 * nseg].y = h; + Vtx[i].x = Vtx[i + nseg].x *= w, Vtx[i].z = Vtx[i + nseg].z *= w; + Vtx[i + 3 * nseg].x = Vtx[i + 2 * nseg].x *= w, Vtx[i + 3 * nseg].z = Vtx[i + 2 * nseg].z *= w; + } + + // rotate vector + VECTOR3 d = end / h; + double tht = acos(d.y), phi = atan2(d.z, d.x); + float cost = (float)cos(tht), sint = (float)sin(tht); + float cosp = (float)cos(phi), sinp = (float)sin(phi); + D3DMATRIX W, R = { cost * cosp, -sint, cost * sinp, 0, + sint * cosp, cost, sint * sinp, 0, + -sinp, 0 , cosp, 0, + 0, 0, 0, 1 }; + + // shift vector + R._41 = (float)orig.x; + R._42 = (float)orig.y; + R._43 = (float)orig.z; + D3DMAT_MatrixMultiply(&W, &mWorld, &R); + dev->SetTransform(D3DTRANSFORMSTATE_WORLD, &W); + + MATRIX3 grot; + VECTOR3 gpos; + oapiGetRotationMatrix(hObj, &grot); + VECTOR3 cp = tmul(grot, -cpos); + if (dotp(d, unit(end - cp)) > 0) + Idx = Idx1, nIdx = nIdx1; + else + Idx = Idx0, nIdx = nIdx0; + + mtrl.emissive.r = 0.5f * (mtrl.diffuse.r = (float)col.x); + mtrl.emissive.g = 0.5f * (mtrl.diffuse.g = (float)col.y); + mtrl.emissive.b = 0.5f * (mtrl.diffuse.b = (float)col.z); + if (opac < 1.0f) + mtrl.emissive.a = mtrl.diffuse.a = mtrl.ambient.a = mtrl.specular.a = opac; + dev->GetMaterial(&pmtrl); + dev->SetMaterial(&mtrl); + dev->SetTexture(0, 0); + dev->SetRenderState(D3DRENDERSTATE_SPECULARENABLE, TRUE); + dev->GetRenderState(D3DRENDERSTATE_ZENABLE, &bZbuf); + if (bZbuf) dev->SetRenderState(D3DRENDERSTATE_ZENABLE, FALSE); + dev->DrawIndexedPrimitive( + D3DPT_TRIANGLELIST, D3DFVF_VERTEX, Vtx, nVtx, Idx, nIdx, 0); + dev->SetMaterial(&pmtrl); + dev->SetRenderState(D3DRENDERSTATE_SPECULARENABLE, FALSE); + dev->SetTransform(D3DTRANSFORMSTATE_WORLD, &mWorld); + if (bZbuf) dev->SetRenderState(D3DRENDERSTATE_ZENABLE, TRUE); + if (opac < 1.0f) + mtrl.emissive.a = mtrl.diffuse.a = mtrl.ambient.a = mtrl.specular.a = 1.0f; + return true; +} diff --git a/OVP/D3D7Client/VObject.h b/OVP/D3D7Client/VObject.h index 9f7796588..cf57a1f18 100644 --- a/OVP/D3D7Client/VObject.h +++ b/OVP/D3D7Client/VObject.h @@ -140,6 +140,12 @@ class vObject: public oapi::VisObject { */ virtual bool Update (); + /** + * \brief Set up any vectors (forces, coordinate axes) to be drawn in the + * next render pass. + */ + virtual void UpdateRenderVectors() {} + /** * \brief Level-of-detail check * \default None. @@ -167,9 +173,31 @@ class vObject: public oapi::VisObject { */ virtual void RenderBeacons (LPDIRECT3DDEVICE7 dev) {} + /** + * \brief structure for rendering vectors + */ + struct BodyVectorRec { + VECTOR3 v; ///< vector definition in local object frame + VECTOR3 orig; ///< vector origin in local object frame + double rad; ///< vector arrow radius + double dist; ///< camera distance + VECTOR3 col; ///< colour components + float alpha; ///< alpha component + std::string label; ///< vector label + DWORD lcol; + float lsize; ///< length scale + }; + + void RenderVectors(LPDIRECT3DDEVICE7 dev); + protected: void RenderSpot (LPDIRECT3DDEVICE7 dev, const VECTOR3 *ofs, float size, const VECTOR3 &col, bool lighting, int shape); + void AddVector(const VECTOR3& v, const VECTOR3& orig, double rad, const std::string& label, const VECTOR3& col, float alpha = 1.0f, DWORD lcol = 0, float lsize = -1.0); + + bool DrawVector(LPDIRECT3DDEVICE7 dev, const VECTOR3& end, const VECTOR3& orig, double rad, + const VECTOR3& col, float opac); + static const oapi::D3D7Client *gc; // graphics client instance pointer static LPDIRECTDRAWSURFACE7 blobtex[3]; // beacon textures @@ -181,6 +209,7 @@ class vObject: public oapi::VisObject { double size; // object radius [m] double cdist; // current camera distance VECTOR3 cpos; // camera-relative object position + std::vector veclist; ///< list of body vectors to be rendered }; #endif // !__VOBJECT_H \ No newline at end of file diff --git a/OVP/D3D7Client/VVessel.cpp b/OVP/D3D7Client/VVessel.cpp index eec2c19c9..231c28038 100644 --- a/OVP/D3D7Client/VVessel.cpp +++ b/OVP/D3D7Client/VVessel.cpp @@ -124,6 +124,56 @@ bool vVessel::Update () return true; } +void vVessel::UpdateRenderVectors() +{ + veclist.clear(); + + DWORD flag = *(DWORD*)gc->GetConfigParam(CFGPRM_FORCEVECTORFLAG); + if (flag & BF_ENABLE) { + VESSEL* vessel = oapiGetVesselInterface(hObj); + static double shift = 1e3, lshift = log(shift); + bool logscale = ((flag & BF_LOGSCALE) != 0); + double len, scale, scale2 = size * 0.02; + scale = (logscale ? 1.0 : size / (vessel->GetMass() * 9.81)); + double m = vessel->GetMass(); // DEBUG + double pscale = *(float*)gc->GetConfigParam(CFGPRM_FORCEVECTORSCALE); + float alpha = *(float*)gc->GetConfigParam(CFGPRM_FORCEVECTOROPACITY); + char cbuf[256]; + VECTOR3 F; + + if ((flag & BF_WEIGHT) && vessel->GetWeightVector(F)) { + sprintf(cbuf, "G = %fN", len = length(F)); + if (logscale) len = log(len + shift) - lshift; else len *= scale; + AddVector(unit(F) * (len * pscale), _V(0, 0, 0), scale2, std::string(cbuf), _V(1, 1, 0), alpha, D3DRGB(1, 1, 0)); + } + if ((flag & BF_THRUST) && vessel->GetThrustVector(F)) { + sprintf(cbuf, "T = %fN", len = length(F)); + if (logscale) len = log(len + shift) - lshift; else len *= scale; + AddVector(unit(F) * (len * pscale), _V(0, 0, 0), scale2, std::string(cbuf), _V(0, 0, 1), alpha, D3DRGB(0.5, 0.5, 1)); + } + if ((flag & BF_LIFT) && vessel->GetLiftVector(F)) { + sprintf(cbuf, "L = %fN", len = length(F)); + if (logscale) len = log(len + shift) - lshift; else len *= scale; + AddVector(unit(F) * (len * pscale), _V(0, 0, 0), scale2, std::string(cbuf), _V(0, 1, 0), alpha, D3DRGB(0.5, 1, 0.5)); + } + if ((flag & BF_DRAG) && vessel->GetDragVector(F)) { + sprintf(cbuf, "D = %fN", len = length(F)); + if (logscale) len = log(len + shift) - lshift; else len *= scale; + AddVector(unit(F) * (len * pscale), _V(0, 0, 0), scale2, std::string(cbuf), _V(1, 0, 0), alpha, D3DRGB(1, 0.5, 0.5)); + } + if ((flag & BF_TOTAL) && vessel->GetForceVector(F)) { + sprintf(cbuf, "F = %fN", len = length(F)); + if (logscale) len = log(len + shift) - lshift; else len *= scale; + AddVector(unit(F) * (len * pscale), _V(0, 0, 0), scale2, std::string(cbuf), _V(1, 1, 1), alpha, D3DRGB(1, 1, 1)); + } + if ((flag & BF_TORQUE) && vessel->GetTorqueVector(F)) { + sprintf(cbuf, "M = %fNm", len = length(F)); + if (logscale) len = log(len + 1e-5) - log(1e-5); else len *= scale * 1e5; + AddVector(unit(F) * (len * pscale), _V(0, 0, 0), scale2, std::string(cbuf), _V(1, 0, 1), alpha, D3DRGB(1, 0, 1)); + } + } +} + void vVessel::LoadMeshes () { if (nmesh) ClearMeshes(); diff --git a/OVP/D3D7Client/VVessel.h b/OVP/D3D7Client/VVessel.h index ae2a135b2..82a335f59 100644 --- a/OVP/D3D7Client/VVessel.h +++ b/OVP/D3D7Client/VVessel.h @@ -61,6 +61,11 @@ class vVessel: public vObject { */ bool Update (); + /** + * \brief Set up the vessel vectors (forces, frame axes) to render + */ + void UpdateRenderVectors(); + /** * \brief Object render call * \param dev render device diff --git a/Orbitersdk/include/GraphicsAPI.h b/Orbitersdk/include/GraphicsAPI.h index 81dec471e..f43600402 100644 --- a/Orbitersdk/include/GraphicsAPI.h +++ b/Orbitersdk/include/GraphicsAPI.h @@ -180,13 +180,35 @@ typedef void *HDC; */ #define CFGPRM_TILEPATCHRES 0x001B -#define CFGPRM_TILELOADFLAGS 0x001C /** * Bitflags for planetary tile file load behaviour * - bit 1: load from individual tiles in directory tree * - bit 2: load from compressed archive file * \par Parameter type: * DWORD + */ +#define CFGPRM_TILELOADFLAGS 0x001C + +/** + * Bit flags for force vector display options. + * \par Parameter type: + * DWORD + */ +#define CFGPRM_FORCEVECTORFLAG 0x001D + +/** + * Force vector display scaling factor + * \par Parameter type: + * float + */ +#define CFGPRM_FORCEVECTORSCALE 0x001E + +/** + * Force vector display opacity value + * \par Parameter type: + * float + */ +#define CFGPRM_FORCEVECTOROPACITY 0x001F /// @} /** @@ -222,6 +244,18 @@ typedef void *HDC; #define PLN_SURFMARK (PLN_BMARK | PLN_RMARK | PLN_LMARK) /// @} +/// \defgroup bfflag Bit flags for vessel force vector render options +/// @{ +#define BF_ENABLE 0x0001 ///< Enable force vectors +#define BF_LOGSCALE 0x0002 ///< Use logarithmic scale (instead of linear scale) +#define BF_WEIGHT 0x0004 ///< Show weight vector +#define BF_THRUST 0x0008 ///< Show thrust vector +#define BF_LIFT 0x0010 ///< Show lift vector +#define BF_DRAG 0x0020 ///< Show drag vector +#define BF_TOTAL 0x0040 ///< Show total force vector +#define BF_TORQUE 0x0080 ///< Show torque vector +/// @} + /// \defgroup bltflag Bit flags for blitting operations /// @{ #define BLT_SRCCOLORKEY 0x1 ///< Use source surface colour key for transparency diff --git a/Src/Orbiter/Config.cpp b/Src/Orbiter/Config.cpp index 16124d62b..04e3d9ba0 100644 --- a/Src/Orbiter/Config.cpp +++ b/Src/Orbiter/Config.cpp @@ -918,6 +918,12 @@ const void *Config::GetParam (DWORD paramtype) const return (void*)&CfgInstrumentPrm.PanelMFDHUDSize; case CFGPRM_TILEPATCHRES: return (void*)&CfgPRenderPrm.PatchRes; + case CFGPRM_FORCEVECTORFLAG: + return (void*)&CfgVisHelpPrm.flagBodyforce; + case CFGPRM_FORCEVECTORSCALE: + return (void*)&CfgVisHelpPrm.scaleBodyforce; + case CFGPRM_FORCEVECTOROPACITY: + return (void*)&CfgVisHelpPrm.opacBodyforce; default: return 0; } diff --git a/Src/Orbiter/Config.h b/Src/Orbiter/Config.h index 4dee645db..a7c46224f 100644 --- a/Src/Orbiter/Config.h +++ b/Src/Orbiter/Config.h @@ -18,16 +18,6 @@ #include #include "GraphicsAPI.h" -// body force modes -#define BF_ENABLE 0x0001 -#define BF_LOGSCALE 0x0002 -#define BF_WEIGHT 0x0004 -#define BF_THRUST 0x0008 -#define BF_LIFT 0x0010 -#define BF_DRAG 0x0020 -#define BF_TOTAL 0x0040 -#define BF_TORQUE 0x0080 - // coordinate axes modes #define CA_ENABLE 0x0001 #define CA_NEG 0x0002