Elite/Oolite HUD display: to OpenGL or not to OpenGL? lazarus / 39-Graphics ---------------------------------------------------------------------- MarkMLl · 2025-04-14 19:19 That is the question. I initially assumed that OpenGL was the best choice for this, but now I'm not so sure. I've been spinning my wheels over the last couple of weeks trying to work my way up the learning curve, but the more I get into it the more problems I encounter. One of the more significant ones is that while one would expect OpenGL to be the natural choice for 3D vector graphics, it turns out that many of the facilities which superficially appear to make it a good choice are deprecated: one is expected to set up custom shaders at multiple levels, and I feel that as far as Pascal support is concerned I'm heading for the bleeding edge. Apart from the current unstaunched goriness, I'm worried that an operating system upgrade might completely remove even more "legacy" functionality, which would obviously break anything that I (or younger commercial successors) was trying to maintain. Adding to that, OpenGL appears to not be good at rendering arbitrary glyphs and (small amounts of) text. What I'm trying to do is render the red part of the attached image, with the ellipse representing a glide path at a nominal 3 degree angle. Now I could obviously draw the ellipse and distance markers on a plane and then set the viewpoint ("camera position") and possibly also the perspective and clipping ("frustum") appropriately, but I'd then have to work out the geometry of the flags ("poles" must be vertical) as an exception to that and do any annotation of what each represented (coloured glyph plus possibly text) "the hard way". Taking those considerations into account, and in particular considering the good support of the LCL canvas for arbitrary text etc., I'm beginning to feel that OpenGL might be more trouble than it's worth. I'd be very interested in anybody else's opinion. MarkMLl ---------------------------------------------------------------------- Guva · 2025-04-15 02:27 Maybe not quite on the topic, but it might come in handy. QuoteManually projecting 3d coordinates onto the 2d screen can be useful as seen above to label objects (although you can do a similar thing with billboards), it can also be used to create a kind of 3d radar effect (think Elite) https://bedroomcoders.co.uk/posts/173 Rewritten pascal code, without shaders for a smaller size Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program RaylibTest; uses  SysUtils, Math,  raylib, raymath; const  screenWidth = 1280;  screenHeight = 720; type  TVector4 = record    x, y, z, w: Single;  end;   PModel = ^TModel;   TObj = record    index: Integer;  pos: TVector3;    proj: TVector4;    m: PModel;  end; function Project(pos: TVector3; matView, matPerps: TMatrix): TVector4;var  temp: TVector4;begin  temp.x := matView.m0*pos.x + matView.m4*pos.y + matView.m8*pos.z + matView.m12;  temp.y := matView.m1*pos.x + matView.m5*pos.y + matView.m9*pos.z + matView.m13;  temp.z := matView.m2*pos.x + matView.m6*pos.y + matView.m10*pos.z + matView.m14;  temp.w := matView.m3*pos.x + matView.m7*pos.y + matView.m11*pos.z + matView.m15;   Result.x := matPerps.m0*temp.x + matPerps.m4*temp.y + matPerps.m8*temp.z + matPerps.m12*temp.w;  Result.y := matPerps.m1*temp.x + matPerps.m5*temp.y + matPerps.m9*temp.z + matPerps.m13*temp.w;  Result.z := matPerps.m2*temp.x + matPerps.m6*temp.y + matPerps.m10*temp.z + matPerps.m14*temp.w;  Result.w := -temp.z;   if Result.w <> 0.0 then  begin    Result.w := (1.0/Result.w)/0.75;  // TODO fudge of .75 WHY???    Result.x := Result.x * Result.w;    Result.y := Result.y * Result.w;    Result.z := Result.z * Result.w;  end;end; var  camera: TCamera;  models: array[0..3] of TModel;  objs: array[0..7] of TObj;  basePos: array[0..7] of TVector3;  labels: array[0..7] of string;  mesh: TMesh;  //shader: TShader;  tex: TTexture;   frame: Integer;  ang: TVector3;  togglePos, toggleRadar: Boolean;  view, perps: TMatrix;  aspect: Single;  i: Integer;  xi, mi: Single;  p: TVector3;  sorted: Boolean;  tmp: TObj;  hsw, hsh: Single;  l: Integer;  ft: string;  GenImage: TImage;begin  // Initialization  InitWindow(screenWidth, screenHeight, 'raylib - test');   // Define the camera  camera.position := Vector3Create(0.0, 1.0, 0.0);  camera.target := Vector3Create(0.0, 0.0, 4.0);  camera.up := Vector3Create(0.0, 1.0, 0.0);  camera.fovy := 45.0;  camera.projection := CAMERA_PERSPECTIVE;   // Initialize objects  for i := 0 to 7 do  begin    objs[i].pos := Vector3Create(Cos(0.7853982*i)*4.0, 0, Sin(0.7853982*i)*4.0);    if (i = 2) or (i = 6) then      objs[i].pos.y := objs[i].pos.y - 0.5;    basePos[i] := objs[i].pos;  objs[i].index := i;  end;   // Labels  labels[0] := 'torus 1'; labels[1] := 'cube 1'; labels[2] := 'cylinder 1'; labels[3] := 'sphere 1';  labels[4] := 'torus 2'; labels[5] := 'cube 2'; labels[6] := 'cylinder 2'; labels[7] := 'sphere 2';   // Create models  mesh := GenMeshTorus(0.3, 1, 16, 32);  models[0] := LoadModelFromMesh(mesh);  mesh := GenMeshCube(1, 1, 1);  models[1] := LoadModelFromMesh(mesh);  mesh := GenMeshCylinder(0.5, 1, 32);  models[2] := LoadModelFromMesh(mesh);  mesh := GenMeshSphere(0.5, 16, 32);  models[3] := LoadModelFromMesh(mesh);   // Assign models to objects  objs[0].m := @models[0]; objs[4].m := @models[0];  objs[1].m := @models[1]; objs[5].m := @models[1];  objs[2].m := @models[2]; objs[6].m := @models[2];  objs[3].m := @models[3]; objs[7].m := @models[3];    GenImage := GenImageChecked(2, 2, 1, 1, RED, GREEN);  tex := LoadTextureFromImage(GenImage);  UnloadImage(GenImage);   for i := 0 to 3 do  begin    models[i].materials[0].maps[MATERIAL_MAP_DIFFUSE].texture := tex;  end;   // Initialize variables  frame := 0;  ang := Vector3Zero();  togglePos := True;  toggleRadar := False;   SetTargetFPS(60);   // Main game loop  while not WindowShouldClose() do  begin    // Update  frame := frame + 1;    ang.x := ang.x + 0.01;    ang.y := ang.y + 0.005;    ang.z := ang.z - 0.0025;     // Rotate model    models[0].transform := MatrixRotateXYZ(ang);     UpdateCamera(@camera, CAMERA_FIRST_PERSON);     // Get matrices for projection    view := MatrixLookAt(camera.position, camera.target, camera.up);    aspect := screenWidth / screenHeight;    perps := MatrixPerspective(camera.fovy, aspect, 0.01, 1000.0);     if IsKeyPressed(KEY_SPACE) then togglePos := not togglePos;    if IsKeyPressed(KEY_R) then toggleRadar := not toggleRadar;     // Update object positions and projections    for i := 0 to 7 do    begin      objs[i].pos := basePos[objs[i].index];    xi := frame / 100.0;      if (objs[i].index mod 2) = 1 then xi := -xi;      mi := 0.25;      if objs[i].index = 0 then mi := 8;      objs[i].pos.x := objs[i].pos.x + Cos(xi) * mi;      objs[i].pos.z := objs[i].pos.z + Sin(xi) * mi;      p := objs[i].pos;      if togglePos then p.y := 1.0 else p.y := 0.0;      objs[i].proj := Project(p, view, perps);    end;     // Sort objects by depth    sorted := False;    while not sorted do    begin      sorted := True;      for i := 0 to 6 do      begin        if objs[i].proj.w > objs[i+1].proj.w then        begin          sorted := False;          tmp := objs[i];          objs[i] := objs[i+1];          objs[i+1] := tmp;        end;      end;    end;     // Draw    BeginDrawing();      ClearBackground(BLACK);       BeginMode3D(camera);        // Draw models        for i := 0 to 7 do        begin          DrawModel(objs[i].m^, objs[i].pos, 1.0, WHITE);        end;        DrawGrid(10, 1.0);      EndMode3D();       DrawFPS(10, 10);      DrawText('Space : toggle label position,  R : toggle 3d radar', 140, 10, 20, DARKGREEN);       hsw := screenWidth / 2.0;      hsh := screenHeight / 2.0;       for i := 0 to 7 do      begin        if objs[i].proj.w > 0 then        begin          // Draw label          l := MeasureText(Pchar(labels[objs[i].index]), 24);          DrawRectangle(Round(hsw + objs[i].proj.x * hsw - 4 - l/2),                      Round(hsh - objs[i].proj.y * hsh - 4),                      l + 8, 27, BLUE);          DrawText(PChar(labels[objs[i].index]),                 Round(hsw + objs[i].proj.x * hsw - l/2),                 Round(hsh - objs[i].proj.y * hsh), 24, WHITE);           // Draw coordinates          ft := Format('%2.3f %2.3f', [objs[i].pos.x, objs[i].pos.z]);          l := MeasureText(PChar(ft), 24);          DrawRectangle(Round(hsw + objs[i].proj.x * hsw - 4 - l/2),                      Round(hsh - objs[i].proj.y * hsh - 4 + 27),                      l + 8, 27, BLUE);          DrawText(PChar(ft),                 Round(hsw + objs[i].proj.x * hsw - l/2),                   Round(hsh - objs[i].proj.y * hsh + 27), 24, WHITE);        end;         if toggleRadar then        begin          // Draw radar          DrawCircle(Round(hsw + (objs[i].proj.x / objs[i].proj.w) * (hsw/32)),                     Round(screenHeight - (hsh/3) - (objs[i].proj.y / objs[i].proj.w) * (hsh/32)),                     3, RED);        end;      end;       // Draw frame counter      ft := Format('Frame %d', [frame]);      l := MeasureText(PChar(ft), 20);      DrawRectangle(16, 698, l + 8, 42, BLUE);      DrawText(PChar(ft), 20, 700, 20, WHITE);  EndDrawing();  end;   // Cleanup  for i := 0 to 3 do    UnloadModel(models[i]);  UnloadTexture(tex);  CloseWindow();end. ---------------------------------------------------------------------- MarkMLl · 2025-04-15 08:13 Thanks, I'll consider that. From https://github.com/GuvaCode/Ray4Laz I note that it claims no external dependencies, so I presume that your advice is to step away from OpenGL before things get messy. MarkMLl ---------------------------------------------------------------------- MarkMLl · 2025-04-15 17:48 I'm inclined to junk OpenGL and simply work on canvases. If anybody has any strong opinions either way I'd be interested to hear them (come on Thaddy, you can never resist that :-) MarkMLl ---------------------------------------------------------------------- Seenkao · 2025-04-15 19:32 Тут всё зависит от того, какого конечного результата вы хотите добиться. Если вас интересует практика и понимание как работает 2D или 3D, то OpenGL очень неплохой выбор в этом случае. Используя его, вы сможете понять как саму 2D/3D графику, так и научиться делать достаточно неплохую графику. Если же вам нужен результат и вы не хотите разбираться как устроена 2D/3D графика, то мало толку от любого контекста, будь то OpenGL, DX, Vulkan, Metal... В этом случае лучше брать готовые фреймворки/библиотеки которые делают за вас большую часть, а вы только используете функционал. Так же есть третий вариант. Использовать OpenGL, но при этом просто взять подсмотреть готовые функции в фреймворках/библиотеках/движках. Ну и напоминаю что, фреймворки/библиотеки/движки, зачастую, содержат в себе функционал больший чем просто работа с графикой. --------------------------------------------------------------- Google translate: Here it all depends on what final result you want to achieve. If you are interested in practice and understanding how 2D or 3D works, then OpenGL is a very good choice in this case. Using it, you will be able to understand both 2D/3D graphics itself and learn how to make pretty good graphics. If you need a result and you do not want to understand how 2D/3D graphics are arranged, then there is little use in any context, be it OpenGL, DX, Vulkan, Metal... In this case, it is better to take ready- made frameworks/libraries that do most of the work for you, and you only use the functionality. There is also a third option. Use OpenGL, but at the same time just take a look at ready-made functions in frameworks/libraries/engines. Well, I remind you that frameworks/libraries/engines often contain functionality greater than just working with graphics. ---------------------------------------------------------------------- MarkMLl · 2025-04-15 20:01 Quote from: Seenkao on April 15, 2025, 09:32:56 pmHere it all depends on what final result you want to achieve. If you are interested in practice and understanding how 2D or 3D works, then OpenGL is a very good choice in this case. Using it, you will be able to understand both 2D/3D graphics itself and learn how to make pretty good graphics. I'm not. I'm specifically interested in achieving that particular graphical presentation, which I know will resonate deeply with potential customers. The technology used to achieve it was 8-bit home computers. MarkMLl