diff --git a/model_viewer/hob_mesh.pas b/model_viewer/hob_mesh.pas new file mode 100644 index 0000000..d734f31 --- /dev/null +++ b/model_viewer/hob_mesh.pas @@ -0,0 +1,454 @@ +unit hob_mesh; +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, gl, GLext, math, gvector, + hob_parser, hmt_parser; + +type + TVertex = record + x, y, z: single; + end; + + TTriangle = record + vertices: array [0..2] of TVertex; + material_index: integer; + tex_coords: array [0..2, 0..1] of single; + colors: array[0..2] of TRGBA; + end; + + TMaterial = record + has_texture: boolean; + bpp: byte; + gl_tex_id: integer; + width, height: integer; + pixels: pbyte; + end; + + TVertexList = specialize TVector; + TTriangleList = specialize TVector; + TMaterialArray = array of TMaterial; + + TRenderOpts = record + wireframe: boolean; + points: boolean; + vcolors: boolean; + textures: boolean; + fg_to_draw: integer; + end; + + { TModel + single HOB mesh + } + + TModel = class + private + _vertices: TVertexList; + _triangles: array of TTriangleList; + _materials: array of TMaterial; + _hmt: THmtFile; + _hmt_loaded: boolean; + procedure HmtRead(const filename: string); + procedure HobRead(const filename: string); + procedure HobReadMesh(const mesh: THobObject); + public + destructor Destroy; override; + procedure Load(const hob_filename, hmt_filename: string); + procedure InitGL; + procedure DrawGL(opts: TRenderOpts); + procedure ExportObj(const obj_name: string); + end; + +implementation + +{ TModel } + +function FixRange(const coord_i16: smallint): single; +begin + result := 0; + if coord_i16 <> 0 then + result := coord_i16 * (1 / 4000); +end; + +function FixUvRange(const coord_i16: smallint): single; +begin + result := 0; + if coord_i16 <> 0 then + result := coord_i16 * (1 / 4096); +end; + + +{ rearrange HOB data, triangulate quads +} +procedure TModel.HobReadMesh(const mesh: THobObject); +var + i: Integer; + fg: THobFaceGroup; + v: TVertex; + group_vertices: TVertexList; + triangle: TTriangle; + fg_idx: integer; + tris: TTriangleList; + last_idx: integer; + + function InitVertex(face: THobFace; offset: integer): TTriangle; + var + i, k: Integer; + begin + for i := 0 to 2 do begin + k := (i + offset) and $3; + result.vertices[i] := group_vertices[face.indices[k]]; + result.colors[i] := face.vertex_colors[k]; + result.tex_coords[i, 0] := FixUvRange(face.tex_coords[k].u); + result.tex_coords[i, 1] := FixUvRange(face.tex_coords[k].v); + end; + result.material_index := face.material_index; + end; + +begin + group_vertices := TVertexList.Create; + setlength(_triangles, Length(mesh.face_groups)); + fg_idx := 0; + last_idx:=0; + for fg in mesh.face_groups do begin + for i := 0 to fg.vertex_count - 1 do begin + v.x := FixRange(fg.vertices[i].x); + v.y := FixRange(fg.vertices[i].y); + v.z := FixRange(fg.vertices[i].z); + + + v.x += fg.transform.x/16; + v.y += fg.transform.y/16; + v.z += fg.transform.z/16; + + + //flip Y for OpenGL coord system, otherwise the model is upside down. + //Flip x coord too, otherwise the model looks mirrored + v.y := -v.y; + v.x := -v.x; + + _vertices.PushBack(v); + group_vertices.PushBack(v); + end; + tris := TTriangleList.Create; + for i := 0 to fg.face_count - 1 do begin + triangle := InitVertex(fg.faces[i], 0); + tris.PushBack(triangle); + if fg.faces[i].ftype <> 3 then begin + triangle := InitVertex(fg.faces[i], 2); + tris.PushBack(triangle); + end; + end; + _triangles[fg_idx] := tris; + fg_idx += 1; + group_vertices.Clear; + last_idx:=fg.fg_group_id; + end; + group_vertices.Free; +end; + + +procedure TModel.HobRead(const filename: string); +var + i: Integer; + hob: THobFile; +begin + hob := ParseHobFile(filename); + for i := 0 to 0 do + HobReadMesh(hob.objects[i]); + WriteLn('vertices: ', _vertices.Size); + //WriteLn('faces (triangulated): ', _triangles.Count); +end; + + +procedure TModel.HmtRead(const filename: string); + procedure SetTexByName (var mat: TMaterial; const name: string); + var + i: integer; + tex: THmtTexture; + begin + mat.has_texture := false; + for i := 0 to _hmt.texture_count - 1 do + if _hmt.textures[i].name_string = name then begin + tex := _hmt.textures[i]; + if not (tex.image.type_ in [0,1,3,4]) then + break; + + mat.bpp := 24; + if tex.image.type_ = 4 then + mat.bpp := 8; + + mat.width := tex.width; + mat.height := tex.height; + mat.pixels := tex.image.pixels; + mat.has_texture := true; + + writeln('material texture found: ', name); + break; + end; + end; +var + i: integer; +begin + _hmt := ParseHmtFile(filename); + SetLength(_materials, _hmt.material_count); + for i := 0 to _hmt.material_count - 1 do + SetTexByName(_materials[i], _hmt.materials[i].name_string); +end; + + +destructor TModel.Destroy; +begin + inherited Destroy; +// _triangles.Free; +end; + +procedure TModel.Load(const hob_filename, hmt_filename: string); +begin + _vertices := TVertexList.Create; + //_triangles := TTriangleList.Create; + WriteLn('Loading mesh file ', hob_filename); + HobRead(hob_filename); + if FileExists(hmt_filename) then begin + WriteLn('Loading material file ', hmt_filename); + HmtRead(hmt_filename); + _hmt_loaded := true; + end else begin + _hmt_loaded := false; + end; +end; + +procedure pnm_save(const fname: string; const p: pbyte; const w, h: integer); +var + f: file; + c: PChar; +Begin + c := PChar(format('P6'#10'%d %d'#10'255'#10, [w, h])); + AssignFile (f, fname); + Rewrite (f, 1); + BlockWrite (f, c^, strlen(c)); + BlockWrite (f, p^, w * h * 3); + CloseFile (f); +end; + +procedure pgm_save(fname: string; p: pbyte; w, h: integer) ; +var + f: file; + c: PChar; +Begin + c := PChar(format('P5'#10'%d %d'#10'255'#10, [w, h])); + AssignFile (f, fname); + Rewrite (f, 1); + BlockWrite (f, c^, strlen(c)); + BlockWrite (f, p^, w * h); + CloseFile (f); +end; + +procedure TModel.InitGL; + + procedure GenTexture(var mat: TMaterial); + begin + glGenTextures(1, @mat.gl_tex_id); + glBindTexture(GL_TEXTURE_2D, mat.gl_tex_id); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + if mat.bpp = 24 then begin + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, mat.width, mat.height, 0, GL_RGB, GL_UNSIGNED_BYTE, mat.pixels); + //pnm_save(IntToStr(mat.gl_tex_id)+'.pnm', mat.pixels, mat.width, mat.height); + end; + if mat.bpp = 8 then begin + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, mat.width, mat.height, 0, GL_RED, GL_UNSIGNED_BYTE, mat.pixels); + //pgm_save(IntToStr(mat.gl_tex_id)+'.pgm', mat.pixels, mat.width, mat.height); + end; + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + end; + +var + i: integer; +begin + if not _hmt_loaded then + exit; + for i := 0 to _hmt.material_count - 1 do begin + if _materials[i].has_texture then + GenTexture(_materials[i]); + end; +end; + + +procedure TModel.DrawGL(opts: TRenderOpts); +var + vert: TVertex; + i, k: integer; + + procedure DrawTri(tri: TTriangle); + var + mat: TMaterial; + k: Integer; + begin + if _hmt_loaded then begin + mat := _materials[tri.material_index]; + if mat.has_texture then begin + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, mat.gl_tex_id); + end else + glDisable(GL_TEXTURE_2D); + end; + glBegin(GL_TRIANGLES); + for k := 0 to 2 do begin + if opts.vcolors then + glColor4ubv(@tri.colors[k]); + if opts.textures then + glTexCoord2fv(@tri.tex_coords[k, 0]); + glVertex3fv(@tri.vertices[k]); + end; + glEnd; + end; + +begin + if opts.wireframe then + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) + else + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + glDisable(GL_TEXTURE_2D); + if opts.points then begin + glBegin( GL_POINTS ); + glColor3f(0, 1, 0); + for i := 0 to _vertices.Size - 1 do begin + vert := _vertices[i]; + glVertex3fv(@vert); + end; + glEnd; + end; + + glColor3f(1, 1, 1); + for k := 0 to Length(_triangles) - 1 do +//k := min(opts.fg_to_draw, Length(_triangles) - 1); + for i := 0 to _triangles[k].Size - 1 do + DrawTri(_triangles[k][i]); +end; + + +const + HeaderComment = 'Exported with HOB viewer'; + DefaultMaterial = 'default'; + + +procedure TModel.ExportObj(const obj_name: string); +const + DesiredUnitSize = 2; +var + objfile: TextFile; + vt: TVertex; + face: TTriangle; + + x, y, z: double; + u, v: double; + + scaling_factor: double; + coord_max: double; + uv_counter: integer; + last_material_index: integer; + + i,j,k: integer; + vertex_counter: Integer; + +function GetMaxCoord: double; +var + vt: TVertex; + i: integer; +begin + result := 0; + for i := 0 to _vertices.Size - 1 do begin + vt := _vertices[i]; + x := abs(vt.x); + y := abs(vt.y); + z := abs(vt.z); + coord_max := Max(z, Max(x, y)); + if coord_max > result then + result := coord_max; + end; +end; + +begin + AssignFile(objfile, obj_name); + Rewrite(objfile); + + writeln(objfile, '# ' + HeaderComment); + writeln(objfile, 'mtllib ', obj_name + '.mtl'); + + //scale pass + scaling_factor := 1; + //scaling_factor := DesiredUnitSize / GetMaxCoord; + + //vertex pass + for k := 0 to Length(_triangles) - 1 do + for i := 0 to _triangles[k].Size - 1 do begin + face := _triangles[k][i]; + for vt in face.vertices do begin + x := (vt.x) * scaling_factor; + y := (vt.y) * scaling_factor; + z := (vt.z) * scaling_factor; + writeln(objfile, 'v ', x:10:6, ' ', y:10:6, ' ', z:10:6); + end; + end; + + //uv pass + for k := 0 to Length(_triangles) - 1 do + for i := 0 to _triangles[k].Size - 1 do begin + face := _triangles[k][i]; + for j := 0 to 2 do begin + u := face.tex_coords[j, 0]; + v := face.tex_coords[j, 1]; + writeln(objfile, 'vt ', u:10:6, ' ', v:10:6); + end; + end; + + //face / material pass + uv_counter := 1; + vertex_counter := 1; + last_material_index := -1; + + for k := 0 to Length(_triangles) - 1 do begin + if _triangles[k].Size = 0 then + continue; + + writeln(objfile, 'g ', k); + + for i := 0 to _triangles[k].Size - 1 do begin + face := _triangles[k][i]; + + if face.material_index <> last_material_index then begin + if face.material_index = -1 then + writeln(objfile, 'usemtl ' + DefaultMaterial) + else + writeln(objfile, 'usemtl material_id', face.material_index); + last_material_index := face.material_index; + end; + + write(objfile, 'f '); + for vt in face.vertices do begin + write(objfile, vertex_counter); + write(objfile, '/', uv_counter); + write(objfile, ' '); + vertex_counter += 1; + uv_counter += 1; + end; + writeln(objfile); + end; + end; + + CloseFile(objfile); + + //SaveMaterials(pdo, obj_name); +end; + +end. + diff --git a/model_viewer/imgui/imgui.pas b/model_viewer/imgui/imgui.pas new file mode 100644 index 0000000..a223bb6 --- /dev/null +++ b/model_viewer/imgui/imgui.pas @@ -0,0 +1,713 @@ +{ +Bindings for dear imgui (AKA ImGui) - a bloat-free graphical user interface library for C++ +Based on cimgui+ImGui 1.49/1.50 +} +unit imgui; + +interface + +{$IFDEF FPC} +{$PACKRECORDS C} +uses dynlibs; +{$ENDIF} + +const + ImguiLibName = 'cimgui.' + SharedSuffix; + +type + bool = boolean; + Pbool = ^bool; + Pbyte = ^byte; + Pdword = ^dword; + + TFloat2 = array[0..1] of single; + TFloat3 = array[0..2] of single; + TFloat4 = array[0..3] of single; + + TLongInt2 = array[0..1] of longint; + TLongInt3 = array[0..2] of longint; + TLongInt4 = array[0..3] of longint; + + PImDrawData = ^ImDrawData; + PImFont = ^ImFont; + PImFontAtlas = ^ImFontAtlas; + PImFontConfig = ^ImFontConfig; + PImGuiContext = Pointer; + PImGuiSizeConstraintCallbackData = ^ImGuiSizeConstraintCallbackData; + PImGuiStorage = ^ImGuiStorage; + PImGuiStyle = ^ImGuiStyle; + PImGuiTextEditCallbackData = ^ImGuiTextEditCallbackData; + + ImVec2 = record + x, y: single; + end; + PImVec2 = ^ImVec2; + ImVec4 = record + x, y, z, w: single; + end; + PImVec4 = ^ImVec4; + + ImU32 = dword; + ImWchar = word; + ImTextureID = pointer; + ImGuiID = ImU32; + ImGuiCol = longint; + ImGuiStyleVar = longint; + ImGuiKey = longint; + ImGuiAlign = longint; + ImGuiColorEditMode = longint; + ImGuiMouseCursor = longint; + ImGuiWindowFlags = longint; + ImGuiSetCond = longint; + ImGuiInputTextFlags = longint; + ImGuiSelectableFlags = longint; + + ImGuiTreeNodeFlags = ( + Selected = 1 shl 0, + Framed = 1 shl 1, + AllowOverlapMode = 1 shl 2, + NoTreePushOnOpen = 1 shl 3, + NoAutoOpenOnLog = 1 shl 4, + DefaultOpen = 1 shl 5, + OpenOnDoubleClick = 1 shl 6, + OpenOnArrow = 1 shl 7, + Leaf = 1 shl 8, + Bullet = 1 shl 9 + ); + + ImGuiKey_ = ( + ImGuiKey_Tab, // for tabbing through fields + ImGuiKey_LeftArrow, // for text edit + ImGuiKey_RightArrow,// for text edit + ImGuiKey_UpArrow, // for text edit + ImGuiKey_DownArrow, // for text edit + ImGuiKey_PageUp, + ImGuiKey_PageDown, + ImGuiKey_Home, // for text edit + ImGuiKey_End, // for text edit + ImGuiKey_Delete, // for text edit + ImGuiKey_Backspace, // for text edit + ImGuiKey_Enter, // for text edit + ImGuiKey_Escape, // for text edit + ImGuiKey_A, // for text edit CTRL+A: select all + ImGuiKey_C, // for text edit CTRL+C: copy + ImGuiKey_V, // for text edit CTRL+V: paste + ImGuiKey_X, // for text edit CTRL+X: cut + ImGuiKey_Y, // for text edit CTRL+Y: redo + ImGuiKey_Z // for text edit CTRL+Z: undo + //ImGuiKey_COUNT // this is unnecessary, if we declare KeyMap as array[ImGuiKey_] + ); + + //structs + ImGuiIO = record + DisplaySize : ImVec2; + DeltaTime : single; + IniSavingRate : single; + IniFilename : Pchar; + LogFilename : Pchar; + MouseDoubleClickTime : single; + MouseDoubleClickMaxDist : single; + MouseDragThreshold : single; + KeyMap : array[ImGuiKey_] of longint; + KeyRepeatDelay : single; + KeyRepeatRate : single; + UserData : pointer; + Fonts : PImFontAtlas; + FontGlobalScale : single; + FontAllowUserScaling : bool; + FontDefault : PImFont; + DisplayFramebufferScale : ImVec2; + DisplayVisibleMin : ImVec2; + DisplayVisibleMax : ImVec2; + OSXBehaviors : bool; + RenderDrawListsFn : procedure (data:PImDrawData);cdecl; + GetClipboardTextFn : function (user_data:pointer):Pchar;cdecl; + SetClipboardTextFn : procedure (user_data:pointer; text:Pchar);cdecl; + ClipboardUserData : pointer; + MemAllocFn : function (sz:size_t):pointer;cdecl; + MemFreeFn : procedure (ptr:pointer);cdecl; + ImeSetInputScreenPosFn : procedure (x:longint; y:longint);cdecl; + ImeWindowHandle : pointer; + MousePos : ImVec2; + MouseDown : array[0..4] of bool; + MouseWheel : single; + MouseDrawCursor : bool; + KeyCtrl : bool; + KeyShift : bool; + KeyAlt : bool; + KeySuper : bool; + KeysDown : array[0..511] of bool; + InputCharacters : array[0..(16+1)-1] of ImWchar; + WantCaptureMouse : bool; + WantCaptureKeyboard : bool; + WantTextInput : bool; + Framerate : single; + MetricsAllocs : longint; + MetricsRenderVertices : longint; + MetricsRenderIndices : longint; + MetricsActiveWindows : longint; + MouseDelta : ImVec2; + end; + PImGuiIO = ^ImGuiIO; + + ImDrawVert = record + pos, uv: ImVec2; + col: ImU32; + end; + PImDrawVert = ^ImDrawVert; + + PImDrawList = ^ImDrawList; + PPImDrawList = ^PImDrawList; + PImDrawIdx = ^ImDrawIdx; + ImDrawIdx = word; + + PImDrawCmd = ^ImDrawCmd; + ImDrawCallback = procedure(parent_list: PImDrawList; cmd: PImDrawCmd); cdecl; + ImDrawCmd = record + ElemCount: longword; + ClipRect: ImVec4; + TextureId: ImTextureID; + UserCallback: ImDrawCallback; + UserCallbackData: Pointer; + end; + + //imgui uses generic T* pointer as Data, so we need to specialize with pointer types + generic ImVector<_T> = record + Size: integer; + Capacity: integer; + Data: _T; + end; + ImVectorDrawCmd = specialize ImVector; + ImVectorDrawIdx = specialize ImVector; + ImVectorDrawVert = specialize ImVector; + ImDrawList = record + CmdBuffer: ImVectorDrawCmd; + IdxBuffer: ImVectorDrawIdx; + VtxBuffer: ImVectorDrawVert; + end; + + ImDrawData = record + Valid: boolean; + CmdLists: PPImDrawList; + CmdListsCount, + TotalVtxCount, + TotalIdxCount: integer; + end; + + ImGuiStyle = record + end; + ImGuiTextEditCallbackData = record + end; + ImGuiSizeConstraintCallbackData = record + end; + ImGuiStorage = record + end; + ImFont = record + end; + ImFontConfig = record + end; + ImFontAtlas = record + end; + + + ImGuiTextEditCallback = function(Data: PImGuiTextEditCallbackData): longint; cdecl; + ImGuiSizeConstraintCallback = procedure(Data: PImGuiSizeConstraintCallbackData); cdecl; + +function igGetIO(): PImGuiIO; cdecl; external ImguiLibName; +function igGetStyle(): PImGuiStyle; cdecl; external ImguiLibName; +function igGetDrawData(): PImDrawData; cdecl; external ImguiLibName; +procedure igNewFrame; cdecl; external ImguiLibName; +procedure igRender; cdecl; external ImguiLibName; +procedure igShutdown; cdecl; external ImguiLibName; +procedure igShowUserGuide; cdecl; external ImguiLibName; +procedure igShowStyleEditor(ref: PImGuiStyle); cdecl; external ImguiLibName; +procedure igShowTestWindow(opened: Pbool); cdecl; external ImguiLibName; +procedure igShowMetricsWindow(opened: Pbool); cdecl; external ImguiLibName; + +{ Window } +function igBegin(Name: PChar; p_open: Pbool = nil; flags: ImGuiWindowFlags = 0): bool; cdecl; external ImguiLibName; +function igBegin2(Name: PChar; p_open: Pbool; size_on_first_use: ImVec2; bg_alpha: single; flags: ImGuiWindowFlags): bool; cdecl; external ImguiLibName; +procedure igEnd; cdecl; external ImguiLibName; +function igBeginChild(str_id: PChar; size: ImVec2; border: bool; extra_flags: ImGuiWindowFlags): bool; cdecl; external ImguiLibName; +function igBeginChildEx(id: ImGuiID; size: ImVec2; border: bool; extra_flags: ImGuiWindowFlags): bool; cdecl; external ImguiLibName; +procedure igEndChild; cdecl; external ImguiLibName; +procedure igGetContentRegionMax(out_: PImVec2); cdecl; external ImguiLibName; +procedure igGetContentRegionAvail(out_: PImVec2); cdecl; external ImguiLibName; +function igGetContentRegionAvailWidth: single; cdecl; external ImguiLibName; +procedure igGetWindowContentRegionMin(out_: PImVec2); cdecl; external ImguiLibName; +procedure igGetWindowContentRegionMax(out_: PImVec2); cdecl; external ImguiLibName; +function igGetWindowContentRegionWidth: single; cdecl; external ImguiLibName; +function igGetWindowDrawList(): PImDrawList; cdecl; external ImguiLibName; +procedure igGetWindowPos(out_: PImVec2); cdecl; external ImguiLibName; +procedure igGetWindowSize(out_: PImVec2); cdecl; external ImguiLibName; +function igGetWindowWidth: single; cdecl; external ImguiLibName; +function igGetWindowHeight: single; cdecl; external ImguiLibName; +function igIsWindowCollapsed: bool; cdecl; external ImguiLibName; +procedure igSetWindowFontScale(scale: single); cdecl; external ImguiLibName; + +procedure igSetNextWindowPos(pos: ImVec2; cond: ImGuiSetCond); cdecl; external ImguiLibName; +procedure igSetNextWindowPosCenter(cond: ImGuiSetCond); cdecl; external ImguiLibName; +procedure igSetNextWindowSize(size: ImVec2; cond: ImGuiSetCond); cdecl; external ImguiLibName; +procedure igSetNextWindowSizeConstraints(size_min: ImVec2; size_max: ImVec2; custom_callback: ImGuiSizeConstraintCallback; custom_callback_data: pointer); cdecl; external ImguiLibName; +procedure igSetNextWindowContentSize(size: ImVec2); cdecl; external ImguiLibName; +procedure igSetNextWindowContentWidth(Width: single); cdecl; external ImguiLibName; +procedure igSetNextWindowCollapsed(collapsed: bool; cond: ImGuiSetCond); cdecl; external ImguiLibName; +procedure igSetNextWindowFocus; cdecl; external ImguiLibName; +procedure igSetWindowPos(pos: ImVec2; cond: ImGuiSetCond); cdecl; external ImguiLibName; +procedure igSetWindowSize(size: ImVec2; cond: ImGuiSetCond); cdecl; external ImguiLibName; +procedure igSetWindowCollapsed(collapsed: bool; cond: ImGuiSetCond); cdecl; external ImguiLibName; +procedure igSetWindowFocus; cdecl; external ImguiLibName; +procedure igSetWindowPosByName(Name: PChar; pos: ImVec2; cond: ImGuiSetCond); cdecl; external ImguiLibName; +procedure igSetWindowSize2(Name: PChar; size: ImVec2; cond: ImGuiSetCond); cdecl; external ImguiLibName; +procedure igSetWindowCollapsed2(Name: PChar; collapsed: bool; cond: ImGuiSetCond); cdecl; external ImguiLibName; +procedure igSetWindowFocus2(Name: PChar); cdecl; external ImguiLibName; + +function igGetScrollX: single; cdecl; external ImguiLibName; +function igGetScrollY: single; cdecl; external ImguiLibName; +function igGetScrollMaxX: single; cdecl; external ImguiLibName; +function igGetScrollMaxY: single; cdecl; external ImguiLibName; +procedure igSetScrollX(scroll_x: single); cdecl; external ImguiLibName; +procedure igSetScrollY(scroll_y: single); cdecl; external ImguiLibName; +procedure igSetScrollHere(center_y_ratio: single); cdecl; external ImguiLibName; +procedure igSetScrollFromPosY(pos_y: single; center_y_ratio: single); cdecl; external ImguiLibName; +procedure igSetKeyboardFocusHere(offset: longint); cdecl; external ImguiLibName; +procedure igSetStateStorage(tree: PImGuiStorage); cdecl; external ImguiLibName; +function igGetStateStorage(): PImGuiStorage; cdecl; external ImguiLibName; + + +{ Parameters stacks (shared) } +procedure igPushFont(font: PImFont); cdecl; external ImguiLibName; +procedure igPopFont; cdecl; external ImguiLibName; +procedure igPushStyleColor(idx: ImGuiCol; col: ImVec4); cdecl; external ImguiLibName; +procedure igPopStyleColor(Count: longint); cdecl; external ImguiLibName; +procedure igPushStyleVar(idx: ImGuiStyleVar; val: single); cdecl; external ImguiLibName; +procedure igPushStyleVarVec(idx: ImGuiStyleVar; val: ImVec2); cdecl; external ImguiLibName; +procedure igPopStyleVar(Count: longint); cdecl; external ImguiLibName; +function igGetFont(): PImFont; cdecl; external ImguiLibName; +function igGetFontSize: single; cdecl; external ImguiLibName; +procedure igGetFontTexUvWhitePixel(pOut: PImVec2); cdecl; external ImguiLibName; +function igGetColorU32(idx: ImGuiCol; alpha_mul: single): ImU32; cdecl; external ImguiLibName; +function igGetColorU32Vec(col: PImVec4): ImU32; cdecl; external ImguiLibName; + +{ Parameters stacks (current window) } +procedure igPushItemWidth(item_width: single); cdecl; external ImguiLibName; +procedure igPopItemWidth; cdecl; external ImguiLibName; +function igCalcItemWidth: single; cdecl; external ImguiLibName; +procedure igPushTextWrapPos(wrap_pos_x: single); cdecl; external ImguiLibName; +procedure igPopTextWrapPos; cdecl; external ImguiLibName; +procedure igPushAllowKeyboardFocus(v: bool); cdecl; external ImguiLibName; +procedure igPopAllowKeyboardFocus; cdecl; external ImguiLibName; +procedure igPushButtonRepeat(_repeat: bool); cdecl; external ImguiLibName; +procedure igPopButtonRepeat; cdecl; external ImguiLibName; + +{ Layout } +procedure igSeparator; cdecl; external ImguiLibName; +procedure igSameLine(pos_x: single = 0; spacing_w: single = 0); cdecl; external ImguiLibName; +procedure igNewLine; cdecl; external ImguiLibName; +procedure igSpacing; cdecl; external ImguiLibName; +procedure igDummy(size: PImVec2); cdecl; external ImguiLibName; +procedure igIndent(indent_w: single); cdecl; external ImguiLibName; +procedure igUnindent(indent_w: single); cdecl; external ImguiLibName; +procedure igBeginGroup; cdecl; external ImguiLibName; +procedure igEndGroup; cdecl; external ImguiLibName; +procedure igGetCursorPos(pOut: PImVec2); cdecl; external ImguiLibName; +function igGetCursorPosX: single; cdecl; external ImguiLibName; +function igGetCursorPosY: single; cdecl; external ImguiLibName; +procedure igSetCursorPos(local_pos: ImVec2); cdecl; external ImguiLibName; +procedure igSetCursorPosX(x: single); cdecl; external ImguiLibName; +procedure igSetCursorPosY(y: single); cdecl; external ImguiLibName; +procedure igGetCursorStartPos(pOut: PImVec2); cdecl; external ImguiLibName; +procedure igGetCursorScreenPos(pOut: PImVec2); cdecl; external ImguiLibName; +procedure igSetCursorScreenPos(pos: ImVec2); cdecl; external ImguiLibName; +procedure igAlignFirstTextHeightToWidgets; cdecl; external ImguiLibName; +function igGetTextLineHeight: single; cdecl; external ImguiLibName; +function igGetTextLineHeightWithSpacing: single; cdecl; external ImguiLibName; +function igGetItemsLineHeightWithSpacing: single; cdecl; external ImguiLibName; + +{Columns } +procedure igColumns(Count: longint; id: PChar; border: bool); cdecl; external ImguiLibName; +procedure igNextColumn; cdecl; external ImguiLibName; +function igGetColumnIndex: longint; cdecl; external ImguiLibName; +function igGetColumnOffset(column_index: longint): single; cdecl; external ImguiLibName; +procedure igSetColumnOffset(column_index: longint; offset_x: single); cdecl; external ImguiLibName; +function igGetColumnWidth(column_index: longint): single; cdecl; external ImguiLibName; +function igGetColumnsCount: longint; cdecl; external ImguiLibName; + +{ ID scopes } +{ If you are creating widgets in a loop you most likely want to push a unique identifier so ImGui can differentiate them } +{ You can also use "##extra" within your widget name to distinguish them from each others (see 'Programmer Guide') } +procedure igPushIdStr(str_id: PChar); cdecl; external ImguiLibName; +procedure igPushIdStrRange(str_begin: PChar; str_end: PChar); cdecl; external ImguiLibName; +procedure igPushIdPtr(ptr_id: pointer); cdecl; external ImguiLibName; +procedure igPushIdInt(int_id: longint); cdecl; external ImguiLibName; +procedure igPopId; cdecl; external ImguiLibName; +function igGetIdStr(str_id: PChar): ImGuiID; cdecl; external ImguiLibName; +function igGetIdStrRange(str_begin: PChar; str_end: PChar): ImGuiID; cdecl; external ImguiLibName; +function igGetIdPtr(ptr_id: pointer): ImGuiID; cdecl; external ImguiLibName; + +{ Widgets } +procedure igText(fmt: PChar; args: array of const); cdecl; external ImguiLibName; +procedure igText(fmt: PChar); cdecl; external ImguiLibName; +//procedure igTextV(fmt:Pchar; args:va_list);cdecl;external ImguiLibName; +procedure igTextColored(col: ImVec4; fmt: PChar; args: array of const); cdecl; external ImguiLibName; +procedure igTextColored(col: ImVec4; fmt: PChar); cdecl; external ImguiLibName; +//procedure igTextColoredV(col:ImVec4; fmt:Pchar; args:va_list);cdecl;external ImguiLibName; +procedure igTextDisabled(fmt: PChar; args: array of const); cdecl; external ImguiLibName; +procedure igTextDisabled(fmt: PChar); cdecl; external ImguiLibName; +//procedure igTextDisabledV(fmt:Pchar; args:va_list);cdecl;external ImguiLibName; +procedure igTextWrapped(fmt: PChar; args: array of const); cdecl; external ImguiLibName; +procedure igTextWrapped(fmt: PChar); cdecl; external ImguiLibName; +//procedure igTextWrappedV(fmt:Pchar; args:va_list);cdecl;external ImguiLibName; +procedure igTextUnformatted(Text: PChar; text_end: PChar); cdecl; external ImguiLibName; +procedure igLabelText(_label: PChar; fmt: PChar; args: array of const); cdecl; external ImguiLibName; +procedure igLabelText(_label: PChar; fmt: PChar); cdecl; external ImguiLibName; +//procedure igLabelTextV(_label:Pchar; fmt:Pchar; args:va_list);cdecl;external ImguiLibName; +procedure igBullet; cdecl; external ImguiLibName; +procedure igBulletText(fmt: PChar; args: array of const); cdecl; external ImguiLibName; +procedure igBulletText(fmt: PChar); cdecl; external ImguiLibName; +//procedure igBulletTextV(fmt:Pchar; args:va_list);cdecl;external ImguiLibName; +function igButton(_label: PChar; size: ImVec2): bool; cdecl; external ImguiLibName; +function igSmallButton(_label: PChar): bool; cdecl; external ImguiLibName; +function igInvisibleButton(str_id: PChar; size: ImVec2): bool; cdecl; external ImguiLibName; +procedure igImage(user_texture_id: ImTextureID; size: ImVec2; uv0: ImVec2; uv1: ImVec2; tint_col: ImVec4; border_col: ImVec4); cdecl; external ImguiLibName; +function igImageButton(user_texture_id: ImTextureID; size: ImVec2; uv0: ImVec2; uv1: ImVec2; frame_padding: longint; bg_col: ImVec4; + tint_col: ImVec4): bool; cdecl; external ImguiLibName; +function igCheckbox(_label: PChar; v: Pbool): bool; cdecl; external ImguiLibName; +function igCheckboxFlags(_label: PChar; flags: Pdword; flags_value: dword): bool; cdecl; external ImguiLibName; +function igRadioButtonBool(_label: PChar; active: bool): bool; cdecl; external ImguiLibName; +function igRadioButton(_label: PChar; v: Plongint; v_button: longint): bool; cdecl; external ImguiLibName; +function igCombo(_label: PChar; current_item: Plongint; items: PPchar; items_count: longint; height_in_items: longint): bool; cdecl; external ImguiLibName; +function igCombo2(_label: PChar; current_item: Plongint; items_separated_by_zeros: PChar; height_in_items: longint): bool; cdecl; external ImguiLibName; + +//todo : func type param +//function igCombo3(_label:Pchar; current_item:Plongint; items_getter:function (data:pointer; idx:longint; out_text:PPchar):bool; data:pointer; items_count:longint; +// height_in_items:longint):bool;cdecl;external ImguiLibName; +function igColorButton(col: ImVec4; small_height: bool; outline_border: bool): bool; cdecl; external ImguiLibName; + +type + TCol3 = array[0..2] of single; //todo : does this work? + TCol4 = array[0..3] of single; +function igColorEdit3(_label: PChar; col: TCol3): bool; cdecl; external ImguiLibName; +function igColorEdit4(_label: PChar; col: TCol4; show_alpha: bool): bool; cdecl; external ImguiLibName; + +procedure igColorEditMode(mode: ImGuiColorEditMode); cdecl; external ImguiLibName; +procedure igPlotLines(_label: PChar; values: Psingle; values_count: longint; values_offset: longint; overlay_text: PChar; scale_min: single; + scale_max: single; graph_size: ImVec2; stride: longint); cdecl; external ImguiLibName; + +//TODO : func type +//procedure igPlotLines2(_label:Pchar; values_getter:function (data:pointer; idx:longint):single; data:pointer; values_count:longint; values_offset:longint; +// overlay_text:Pchar; scale_min:single; scale_max:single; graph_size:ImVec2);cdecl;external ImguiLibName; +procedure igPlotHistogram(_label: PChar; values: Psingle; values_count: longint; values_offset: longint; overlay_text: PChar; scale_min: single; + scale_max: single; graph_size: ImVec2; stride: longint); cdecl; external ImguiLibName; + +//TODO : func type +//procedure igPlotHistogram2(_label:Pchar; values_getter:function (data:pointer; idx:longint):single; data:pointer; values_count:longint; values_offset:longint; +// overlay_text:Pchar; scale_min:single; scale_max:single; graph_size:ImVec2);cdecl;external ImguiLibName; +procedure igProgressBar(fraction: single; size_arg: PImVec2; overlay: PChar); cdecl; external ImguiLibName; + +{ Widgets: Sliders (tip: ctrl+click on a slider to input text) } +function igSliderFloat(_label: PChar; v: Psingle; v_min: single; v_max: single; display_format: PChar; power: single): bool; cdecl; external ImguiLibName; +function igSliderFloat2(_label: PChar; v: TFloat2; v_min: single; v_max: single; display_format: PChar; power: single): bool; cdecl; external ImguiLibName; +function igSliderFloat3(_label: PChar; v: TFloat3; v_min: single; v_max: single; display_format: PChar; power: single): bool; cdecl; external ImguiLibName; +function igSliderFloat4(_label: PChar; v: TFloat4; v_min: single; v_max: single; display_format: PChar; power: single): bool; cdecl; external ImguiLibName; +function igSliderAngle(_label: PChar; v_rad: Psingle; v_degrees_min: single; v_degrees_max: single): bool; cdecl; external ImguiLibName; +function igSliderInt(_label: PChar; v: Plongint; v_min: longint; v_max: longint; display_format: PChar): bool; cdecl; external ImguiLibName; +function igSliderInt2(_label: PChar; v: TLongInt2; v_min: longint; v_max: longint; display_format: PChar): bool; cdecl; external ImguiLibName; +function igSliderInt3(_label: PChar; v: TLongInt3; v_min: longint; v_max: longint; display_format: PChar): bool; cdecl; external ImguiLibName; +function igSliderInt4(_label: PChar; v: TLongInt4; v_min: longint; v_max: longint; display_format: PChar): bool; cdecl; external ImguiLibName; +function igVSliderFloat(_label: PChar; size: ImVec2; v: Psingle; v_min: single; v_max: single; display_format: PChar; power: single): bool; cdecl; external ImguiLibName; +function igVSliderInt(_label: PChar; size: ImVec2; v: Plongint; v_min: longint; v_max: longint; display_format: PChar): bool; cdecl; external ImguiLibName; + +{ Widgets: Drags (tip: ctrl+click on a drag box to input text) } +// For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, remember than a 'float v[3]' function argument is the same as 'float* v'. You can pass address of your first element out of a contiguous set, e.g. &myvector.x +{ If v_max >= v_max we have no bound } +function igDragFloat(_label: PChar; v: Psingle; v_speed: single; v_min: single; v_max: single; display_format: PChar; power: single): bool; cdecl; external ImguiLibName; +function igDragFloat2(_label: PChar; v: TFloat2; v_speed: single; v_min: single; v_max: single; display_format: PChar; power: single): bool; cdecl; external ImguiLibName; +function igDragFloat3(_label: PChar; v: TFloat3; v_speed: single; v_min: single; v_max: single; display_format: PChar; power: single): bool; cdecl; external ImguiLibName; +function igDragFloat4(_label: PChar; v: TFloat4; v_speed: single; v_min: single; v_max: single; display_format: PChar; power: single): bool; cdecl; external ImguiLibName; +function igDragFloatRange2(_label: PChar; v_current_min: Psingle; v_current_max: Psingle; v_speed: single; v_min: single; v_max: single; + display_format: PChar; display_format_max: PChar; power: single): bool; cdecl; external ImguiLibName; +{ If v_max >= v_max we have no bound } +function igDragInt(_label: PChar; v: Plongint; v_speed: single; v_min: longint; v_max: longint; display_format: PChar): bool; cdecl; external ImguiLibName; +function igDragInt2(_label: PChar; v: TLongInt2; v_speed: single; v_min: longint; v_max: longint; display_format: PChar): bool; cdecl; external ImguiLibName; +function igDragInt3(_label: PChar; v: TLongInt3; v_speed: single; v_min: longint; v_max: longint; display_format: PChar): bool; cdecl; external ImguiLibName; +function igDragInt4(_label: PChar; v: TLongInt4; v_speed: single; v_min: longint; v_max: longint; display_format: PChar): bool; cdecl; external ImguiLibName; +function igDragIntRange2(_label: PChar; v_current_min: Plongint; v_current_max: Plongint; v_speed: single; v_min: longint; v_max: longint; + display_format: PChar; display_format_max: PChar): bool; cdecl; external ImguiLibName; + +{ Widgets: Input } +function igInputText(_label: PChar; buf: PChar; buf_size: size_t; flags: ImGuiInputTextFlags; callback: ImGuiTextEditCallback; + user_data: pointer): bool; cdecl; external ImguiLibName; +function igInputTextMultiline(_label: PChar; buf: PChar; buf_size: size_t; size: ImVec2; flags: ImGuiInputTextFlags; callback: ImGuiTextEditCallback; + user_data: pointer): bool; cdecl; external ImguiLibName; +function igInputFloat(_label: PChar; v: Psingle; step: single; step_fast: single; decimal_precision: longint; extra_flags: ImGuiInputTextFlags): bool; + cdecl; external ImguiLibName; +function igInputFloat2(_label: PChar; v: TFloat2; decimal_precision: longint; extra_flags: ImGuiInputTextFlags): bool; cdecl; external ImguiLibName; +function igInputFloat3(_label: PChar; v: TFloat3; decimal_precision: longint; extra_flags: ImGuiInputTextFlags): bool; cdecl; external ImguiLibName; +function igInputFloat4(_label: PChar; v: TFloat4; decimal_precision: longint; extra_flags: ImGuiInputTextFlags): bool; cdecl; external ImguiLibName; +function igInputInt(_label: PChar; v: Plongint; step: longint; step_fast: longint; extra_flags: ImGuiInputTextFlags): bool; cdecl; external ImguiLibName; +function igInputInt2(_label: PChar; v: TLongInt2; extra_flags: ImGuiInputTextFlags): bool; cdecl; external ImguiLibName; +function igInputInt3(_label: PChar; v: TLongInt3; extra_flags: ImGuiInputTextFlags): bool; cdecl; external ImguiLibName; +function igInputInt4(_label: PChar; v: TLongInt4; extra_flags: ImGuiInputTextFlags): bool; cdecl; external ImguiLibName; + +{ Widgets: Trees } +function igTreeNode(_label: PChar): bool; cdecl; external ImguiLibName; +function igTreeNodeStr(str_id: PChar; fmt: PChar; args: array of const): bool; cdecl; external ImguiLibName; +function igTreeNodeStr(str_id: PChar; fmt: PChar): bool; cdecl; external ImguiLibName; +function igTreeNodePtr(ptr_id: pointer; fmt: PChar; args: array of const): bool; cdecl; external ImguiLibName; +function igTreeNodePtr(ptr_id: pointer; fmt: PChar): bool; cdecl; external ImguiLibName; +//todo : vargs +// function igTreeNodeStrV(str_id:Pchar; fmt:Pchar; args:va_list):bool;cdecl;external ImguiLibName; +//todo : vargs +// function igTreeNodePtrV(ptr_id:pointer; fmt:Pchar; args:va_list):bool;cdecl;external ImguiLibName; +function igTreeNodeEx(_label: PChar; flags: ImGuiTreeNodeFlags): bool; cdecl; external ImguiLibName; +function igTreeNodeExStr(str_id: PChar; flags: ImGuiTreeNodeFlags; fmt: PChar; args: array of const): bool; cdecl; external ImguiLibName; +function igTreeNodeExStr(str_id: PChar; flags: ImGuiTreeNodeFlags; fmt: PChar): bool; cdecl; external ImguiLibName; +function igTreeNodeExPtr(ptr_id: pointer; flags: ImGuiTreeNodeFlags; fmt: PChar; args: array of const): bool; cdecl; external ImguiLibName; +function igTreeNodeExPtr(ptr_id: pointer; flags: ImGuiTreeNodeFlags; fmt: PChar): bool; cdecl; external ImguiLibName; +//todo : vargs +// function igTreeNodeExV(str_id:Pchar; flags:ImGuiTreeNodeFlags; fmt:Pchar; args:va_list):bool;cdecl;external ImguiLibName; +//todo : vargs +// function igTreeNodeExVPtr(ptr_id:pointer; flags:ImGuiTreeNodeFlags; fmt:Pchar; args:va_list):bool;cdecl;external ImguiLibName; +procedure igTreePushStr(str_id: PChar); cdecl; external ImguiLibName; +procedure igTreePushPtr(ptr_id: pointer); cdecl; external ImguiLibName; +procedure igTreePop; cdecl; external ImguiLibName; +procedure igTreeAdvanceToLabelPos; cdecl; external ImguiLibName; +function igGetTreeNodeToLabelSpacing: single; cdecl; external ImguiLibName; +procedure igSetNextTreeNodeOpen(opened: bool; cond: ImGuiSetCond); cdecl; external ImguiLibName; +function igCollapsingHeader(_label: PChar; flags: ImGuiTreeNodeFlags): bool; cdecl; external ImguiLibName; +function igCollapsingHeaderEx(_label: PChar; p_open: Pbool; flags: ImGuiTreeNodeFlags): bool; cdecl; external ImguiLibName; + +{ Widgets: Selectable / Lists } +function igSelectable(_label: PChar; selected: bool; flags: ImGuiSelectableFlags; size: ImVec2): bool; cdecl; external ImguiLibName; +function igSelectableEx(_label: PChar; p_selected: Pbool; flags: ImGuiSelectableFlags; size: ImVec2): bool; cdecl; external ImguiLibName; +function igListBox(_label: PChar; current_item: Plongint; items: PPchar; items_count: longint; height_in_items: longint): bool; cdecl; external ImguiLibName; +//todo : func type +// function igListBox2(_label:Pchar; current_item:Plongint; items_getter:function (data:pointer; idx:longint; out_text:PPchar):bool; data:pointer; items_count:longint; +// height_in_items:longint):bool;cdecl;external ImguiLibName; +function igListBoxHeader(_label: PChar; size: ImVec2): bool; cdecl; external ImguiLibName; +function igListBoxHeader2(_label: PChar; items_count: longint; height_in_items: longint): bool; cdecl; external ImguiLibName; +procedure igListBoxFooter; cdecl; external ImguiLibName; + +{ Widgets: Value() Helpers. Output single value in "name: value" format (tip: freely declare your own within the ImGui namespace!) } +procedure igValueBool(prefix: PChar; b: bool); cdecl; external ImguiLibName; +procedure igValueInt(prefix: PChar; v: longint); cdecl; external ImguiLibName; +procedure igValueUInt(prefix: PChar; v: dword); cdecl; external ImguiLibName; +procedure igValueFloat(prefix: PChar; v: single; float_format: PChar); cdecl; external ImguiLibName; +procedure igValueColor(prefix: PChar; v: ImVec4); cdecl; external ImguiLibName; +procedure igValueColor2(prefix: PChar; v: dword); cdecl; external ImguiLibName; + +{ Tooltip } +procedure igSetTooltip(fmt: PChar; args: array of const); cdecl; external ImguiLibName; +procedure igSetTooltip(fmt: PChar); cdecl; external ImguiLibName; +//todo : vargs +// procedure igSetTooltipV(fmt:Pchar; args:va_list);cdecl;external ImguiLibName; +procedure igBeginTooltip; cdecl; external ImguiLibName; +procedure igEndTooltip; cdecl; external ImguiLibName; + +{ Widgets: Menus } +function igBeginMainMenuBar: bool; cdecl; external ImguiLibName; +procedure igEndMainMenuBar; cdecl; external ImguiLibName; +function igBeginMenuBar: bool; cdecl; external ImguiLibName; +procedure igEndMenuBar; cdecl; external ImguiLibName; +function igBeginMenu(_label: PChar; Enabled: bool): bool; cdecl; external ImguiLibName; +procedure igEndMenu; cdecl; external ImguiLibName; +function igMenuItem(_label: PChar; shortcut: PChar; selected: bool; Enabled: bool): bool; cdecl; external ImguiLibName; +function igMenuItemPtr(_label: PChar; shortcut: PChar; p_selected: Pbool; Enabled: bool): bool; cdecl; external ImguiLibName; + +{ Popup } +procedure igOpenPopup(str_id: PChar); cdecl; external ImguiLibName; +function igBeginPopup(str_id: PChar): bool; cdecl; external ImguiLibName; +function igBeginPopupModal(Name: PChar; p_open: Pbool; extra_flags: ImGuiWindowFlags): bool; cdecl; external ImguiLibName; +function igBeginPopupContextItem(str_id: PChar; mouse_button: longint): bool; cdecl; external ImguiLibName; +function igBeginPopupContextWindow(also_over_items: bool; str_id: PChar; mouse_button: longint): bool; cdecl; external ImguiLibName; +function igBeginPopupContextVoid(str_id: PChar; mouse_button: longint): bool; cdecl; external ImguiLibName; +procedure igEndPopup; cdecl; external ImguiLibName; +procedure igCloseCurrentPopup; cdecl; external ImguiLibName; + +{ Logging: all text output from interface is redirected to tty/file/clipboard. Tree nodes are automatically opened. } +procedure igLogToTTY(max_depth: longint); cdecl; external ImguiLibName; +procedure igLogToFile(max_depth: longint; filename: PChar); cdecl; external ImguiLibName; +procedure igLogToClipboard(max_depth: longint); cdecl; external ImguiLibName; +procedure igLogFinish; cdecl; external ImguiLibName; +procedure igLogButtons; cdecl; external ImguiLibName; +procedure igLogText(fmt: PChar; args: array of const); cdecl; external ImguiLibName; +procedure igLogText(fmt: PChar); cdecl; external ImguiLibName; + +{ Clipping } +procedure igPushClipRect(clip_rect_min: ImVec2; clip_rect_max: ImVec2; intersect_with_current_clip_rect: bool); cdecl; external ImguiLibName; +procedure igPopClipRect; cdecl; external ImguiLibName; + +{ Utilities } +function igIsItemHovered: bool; cdecl; external ImguiLibName; +function igIsItemHoveredRect: bool; cdecl; external ImguiLibName; +function igIsItemActive: bool; cdecl; external ImguiLibName; +function igIsItemClicked(mouse_button: longint): bool; cdecl; external ImguiLibName; +function igIsItemVisible: bool; cdecl; external ImguiLibName; +function igIsAnyItemHovered: bool; cdecl; external ImguiLibName; +function igIsAnyItemActive: bool; cdecl; external ImguiLibName; +procedure igGetItemRectMin(pOut: PImVec2); cdecl; external ImguiLibName; +procedure igGetItemRectMax(pOut: PImVec2); cdecl; external ImguiLibName; +procedure igGetItemRectSize(pOut: PImVec2); cdecl; external ImguiLibName; +procedure igSetItemAllowOverlap; cdecl; external ImguiLibName; +function igIsWindowHovered: bool; cdecl; external ImguiLibName; +function igIsWindowFocused: bool; cdecl; external ImguiLibName; +function igIsRootWindowFocused: bool; cdecl; external ImguiLibName; +function igIsRootWindowOrAnyChildFocused: bool; cdecl; external ImguiLibName; +function igIsRootWindowOrAnyChildHovered: bool; cdecl; external ImguiLibName; +function igIsRectVisible(item_size: ImVec2): bool; cdecl; external ImguiLibName; +function igIsPosHoveringAnyWindow(pos: ImVec2): bool; cdecl; external ImguiLibName; +function igGetTime: single; cdecl; external ImguiLibName; +function igGetFrameCount: longint; cdecl; external ImguiLibName; +function igGetStyleColName(idx: ImGuiCol): PChar; cdecl; external ImguiLibName; +procedure igCalcItemRectClosestPoint(pOut: PImVec2; pos: ImVec2; on_edge: bool; outward: single); cdecl; external ImguiLibName; +procedure igCalcTextSize(pOut: PImVec2; Text: PChar; text_end: PChar; hide_text_after_double_hash: bool; wrap_width: single); cdecl; external ImguiLibName; +procedure igCalcListClipping(items_count: longint; items_height: single; out_items_display_start: Plongint; out_items_display_end: Plongint); cdecl; external ImguiLibName; + +function igBeginChildFrame(id: ImGuiID; size: ImVec2; extra_flags: ImGuiWindowFlags): bool; cdecl; external ImguiLibName; +procedure igEndChildFrame; cdecl; external ImguiLibName; + +procedure igColorConvertU32ToFloat4(pOut: PImVec4; in_: ImU32); cdecl; external ImguiLibName; +function igColorConvertFloat4ToU32(in_: ImVec4): ImU32; cdecl; external ImguiLibName; +procedure igColorConvertRGBtoHSV(r: single; g: single; b: single; out_h: Psingle; out_s: Psingle; out_v: Psingle); cdecl; external ImguiLibName; +procedure igColorConvertHSVtoRGB(h: single; s: single; v: single; out_r: Psingle; out_g: Psingle; out_b: Psingle); cdecl; external ImguiLibName; + +function igGetKeyIndex(key: ImGuiKey): longint; cdecl; external ImguiLibName; +function igIsKeyDown(key_index: longint): bool; cdecl; external ImguiLibName; +function igIsKeyPressed(key_index: longint; _repeat: bool): bool; cdecl; external ImguiLibName; +function igIsKeyReleased(key_index: longint): bool; cdecl; external ImguiLibName; +function igIsMouseDown(button: longint): bool; cdecl; external ImguiLibName; +function igIsMouseClicked(button: longint; _repeat: bool): bool; cdecl; external ImguiLibName; +function igIsMouseDoubleClicked(button: longint): bool; cdecl; external ImguiLibName; +function igIsMouseReleased(button: longint): bool; cdecl; external ImguiLibName; +function igIsMouseHoveringWindow: bool; cdecl; external ImguiLibName; +function igIsMouseHoveringAnyWindow: bool; cdecl; external ImguiLibName; +function igIsMouseHoveringRect(r_min: ImVec2; r_max: ImVec2; clip: bool): bool; cdecl; external ImguiLibName; +function igIsMouseDragging(button: longint; lock_threshold: single): bool; cdecl; external ImguiLibName; +procedure igGetMousePos(pOut: PImVec2); cdecl; external ImguiLibName; +procedure igGetMousePosOnOpeningCurrentPopup(pOut: PImVec2); cdecl; external ImguiLibName; +procedure igGetMouseDragDelta(pOut: PImVec2; button: longint; lock_threshold: single); cdecl; external ImguiLibName; +procedure igResetMouseDragDelta(button: longint); cdecl; external ImguiLibName; +function igGetMouseCursor: ImGuiMouseCursor; cdecl; external ImguiLibName; +procedure igSetMouseCursor(_type: ImGuiMouseCursor); cdecl; external ImguiLibName; +procedure igCaptureKeyboardFromApp(capture: bool); cdecl; external ImguiLibName; +procedure igCaptureMouseFromApp(capture: bool); cdecl; external ImguiLibName; + +{ Helpers functions to access functions pointers in ImGui::GetIO() } +function igMemAlloc(sz: size_t): pointer; cdecl; external ImguiLibName; +procedure igMemFree(ptr: pointer); cdecl; external ImguiLibName; +function igGetClipboardText: PChar; cdecl; external ImguiLibName; +procedure igSetClipboardText(Text: PChar); cdecl; external ImguiLibName; + +{ Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself } +function igGetVersion(): PChar; cdecl; external ImguiLibName; + +procedure ImFontConfig_DefaultConstructor(config: PImFontConfig); cdecl; external ImguiLibName; + +procedure ImFontAtlas_GetTexDataAsRGBA32(atlas: PImFontAtlas; out_pixels: PPByte; out_width, out_height: PInteger; out_bytes_per_pixel: PInteger = nil); cdecl; external ImguiLibName; +procedure ImFontAtlas_GetTexDataAsAlpha8(atlas: PImFontAtlas; out_pixels: PPByte; out_width, out_height: PInteger; out_bytes_per_pixel: PInteger = nil); cdecl; external ImguiLibName; +procedure ImFontAtlas_SetTexID(atlas: PImFontAtlas; tex: Pointer); cdecl; external ImguiLibName; +function ImFontAtlas_AddFontDefault(atlas: PImFontAtlas; config: PImFontConfig = nil): PImFont; cdecl; external ImguiLibName; +{todo +function ImFontAtlas_AddFont(struct ImFontAtlas* atlas, CONST struct ImFontConfig* font_cfg): PImFont; +function ImFontAtlas_AddFontFromFileTTF(struct ImFontAtlas* atlas, CONST char* filename, float size_pixels, CONST struct ImFontConfig* font_cfg, CONST ImWchar* glyph_ranges): PImFont; +function ImFontAtlas_AddFontFromMemoryTTF(struct ImFontAtlas* atlas, void* ttf_data, int ttf_size, float size_pixels, CONST struct ImFontConfig* font_cfg, CONST ImWchar* glyph_ranges): PImFont; +function ImFontAtlas_AddFontFromMemoryCompressedTTF(struct ImFontAtlas* atlas, CONST void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, CONST struct ImFontConfig* font_cfg, CONST ImWchar* glyph_ranges): PImFont; +function ImFontAtlas_AddFontFromMemoryCompressedBase85TTF(struct ImFontAtlas* atlas, CONST char* compressed_ttf_data_base85, float size_pixels, CONST struct ImFontConfig* font_cfg, CONST ImWchar* glyph_ranges): PImFont; +} +procedure ImFontAtlas_ClearTexData(atlas: PImFontAtlas); cdecl; external ImguiLibName; +procedure ImFontAtlas_Clear(atlas: PImFontAtlas); cdecl; external ImguiLibName; + +procedure ImGuiIO_AddInputCharacter(c: word); cdecl; external ImguiLibName; +procedure ImGuiIO_AddInputCharactersUTF8(utf8_chars: pchar); cdecl; external ImguiLibName; +procedure ImGuiIO_ClearInputCharacters(); cdecl; external ImguiLibName; + +{ImDrawData } +procedure ImDrawData_DeIndexAllBuffers(drawData: PImDrawData); cdecl; external ImguiLibName; + +{ImDrawList } +function ImDrawList_GetVertexBufferSize(list: PImDrawList): longint; cdecl; external ImguiLibName; +function ImDrawList_GetVertexPtr(list: PImDrawList; n: longint): PImDrawVert; external ImguiLibName; +function ImDrawList_GetIndexBufferSize(list: PImDrawList): longint; cdecl; external ImguiLibName; +function ImDrawList_GetIndexPtr(list: PImDrawList; n: longint): PImDrawIdx; cdecl; external ImguiLibName; +function ImDrawList_GetCmdSize(list: PImDrawList): longint; cdecl; external ImguiLibName; +function ImDrawList_GetCmdPtr(list: PImDrawList; n: longint): PImDrawCmd; external ImguiLibName; + +procedure ImDrawList_Clear(list: PImDrawList); cdecl; external ImguiLibName; +procedure ImDrawList_ClearFreeMemory(list: PImDrawList); cdecl; external ImguiLibName; +procedure ImDrawList_PushClipRect(list: PImDrawList; clip_rect_min: ImVec2; clip_rect_max: ImVec2; intersect_with_current_clip_rect: bool); cdecl; external ImguiLibName; +procedure ImDrawList_PushClipRectFullScreen(list: PImDrawList); cdecl; external ImguiLibName; +procedure ImDrawList_PopClipRect(list: PImDrawList); cdecl; external ImguiLibName; +procedure ImDrawList_PushTextureID(list: PImDrawList; texture_id: ImTextureID); cdecl; external ImguiLibName; +procedure ImDrawList_PopTextureID(list: PImDrawList); cdecl; external ImguiLibName; + +{ Primitives } +procedure ImDrawList_AddLine(list: PImDrawList; a: ImVec2; b: ImVec2; col: ImU32; thickness: single); cdecl; external ImguiLibName; +procedure ImDrawList_AddRect(list: PImDrawList; a: ImVec2; b: ImVec2; col: ImU32; rounding: single; rounding_corners: longint; thickness: single); cdecl; external ImguiLibName; +procedure ImDrawList_AddRectFilled(list: PImDrawList; a: ImVec2; b: ImVec2; col: ImU32; rounding: single; rounding_corners: longint); cdecl; external ImguiLibName; +procedure ImDrawList_AddRectFilledMultiColor(list: PImDrawList; a: ImVec2; b: ImVec2; col_upr_left: ImU32; col_upr_right: ImU32; + col_bot_right: ImU32; col_bot_left: ImU32); cdecl; external ImguiLibName; +procedure ImDrawList_AddQuad(list: PImDrawList; a: ImVec2; b: ImVec2; c: ImVec2; d: ImVec2; col: ImU32; thickness: single); cdecl; external ImguiLibName; +procedure ImDrawList_AddQuadFilled(list: PImDrawList; a: ImVec2; b: ImVec2; c: ImVec2; d: ImVec2; col: ImU32); cdecl; external ImguiLibName; +procedure ImDrawList_AddTriangle(list: PImDrawList; a: ImVec2; b: ImVec2; c: ImVec2; col: ImU32; thickness: single); cdecl; external ImguiLibName; +procedure ImDrawList_AddTriangleFilled(list: PImDrawList; a: ImVec2; b: ImVec2; c: ImVec2; col: ImU32); cdecl; external ImguiLibName; +procedure ImDrawList_AddCircle(list: PImDrawList; centre: ImVec2; radius: single; col: ImU32; num_segments: longint; thickness: single); cdecl; external ImguiLibName; +procedure ImDrawList_AddCircleFilled(list: PImDrawList; centre: ImVec2; radius: single; col: ImU32; num_segments: longint); cdecl; external ImguiLibName; +procedure ImDrawList_AddText(list: PImDrawList; pos: ImVec2; col: ImU32; text_begin: PChar; text_end: PChar); cdecl; external ImguiLibName; +procedure ImDrawList_AddTextExt(list: PImDrawList; font: PImFont; font_size: single; pos: ImVec2; col: ImU32; text_begin: PChar; + text_end: PChar; wrap_width: single; cpu_fine_clip_rect: PImVec4); cdecl; external ImguiLibName; +procedure ImDrawList_AddImage(list: PImDrawList; user_texture_id: ImTextureID; a: ImVec2; b: ImVec2; uv0: ImVec2; uv1: ImVec2; col: ImU32); cdecl; external ImguiLibName; +procedure ImDrawList_AddPolyline(list: PImDrawList; points: PImVec2; num_points: longint; col: ImU32; closed: bool; thickness: single; + anti_aliased: bool); cdecl; external ImguiLibName; +procedure ImDrawList_AddConvexPolyFilled(list: PImDrawList; points: PImVec2; num_points: longint; col: ImU32; anti_aliased: bool); cdecl; external ImguiLibName; +procedure ImDrawList_AddBezierCurve(list: PImDrawList; pos0: ImVec2; cp0: ImVec2; cp1: ImVec2; pos1: ImVec2; col: ImU32; thickness: single; + num_segments: longint); cdecl; external ImguiLibName; + +{ Stateful path API, add points then finish with PathFill() or PathStroke() } +procedure ImDrawList_PathClear(list: PImDrawList); cdecl; external ImguiLibName; +procedure ImDrawList_PathLineTo(list: PImDrawList; pos: ImVec2); cdecl; external ImguiLibName; +procedure ImDrawList_PathLineToMergeDuplicate(list: PImDrawList; pos: ImVec2); cdecl; external ImguiLibName; +procedure ImDrawList_PathFill(list: PImDrawList; col: ImU32); cdecl; external ImguiLibName; +procedure ImDrawList_PathStroke(list: PImDrawList; col: ImU32; closed: bool; thickness: single); cdecl; external ImguiLibName; +procedure ImDrawList_PathArcTo(list: PImDrawList; centre: ImVec2; radius: single; a_min: single; a_max: single; num_segments: longint); cdecl; external ImguiLibName; +{ Use precomputed angles for a 12 steps circle } +procedure ImDrawList_PathArcToFast(list: PImDrawList; centre: ImVec2; radius: single; a_min_of_12: longint; a_max_of_12: longint); cdecl; external ImguiLibName; +procedure ImDrawList_PathBezierCurveTo(list: PImDrawList; p1: ImVec2; p2: ImVec2; p3: ImVec2; num_segments: longint); cdecl; external ImguiLibName; +procedure ImDrawList_PathRect(list: PImDrawList; rect_min: ImVec2; rect_max: ImVec2; rounding: single; rounding_corners: longint); cdecl; external ImguiLibName; + +{ Channels } +procedure ImDrawList_ChannelsSplit(list: PImDrawList; channels_count: longint); cdecl; external ImguiLibName; +procedure ImDrawList_ChannelsMerge(list: PImDrawList); cdecl; external ImguiLibName; +procedure ImDrawList_ChannelsSetCurrent(list: PImDrawList; channel_index: longint); cdecl; external ImguiLibName; + +{ Advanced } +{ Your rendering function must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles. } +procedure ImDrawList_AddCallback(list: PImDrawList; callback: ImDrawCallback; callback_data: pointer); cdecl; external ImguiLibName; +{ This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible } +procedure ImDrawList_AddDrawCmd(list: PImDrawList); cdecl; external ImguiLibName; + +{ Internal helpers } +procedure ImDrawList_PrimReserve(list: PImDrawList; idx_count: longint; vtx_count: longint); cdecl; external ImguiLibName; +procedure ImDrawList_PrimRect(list: PImDrawList; a: ImVec2; b: ImVec2; col: ImU32); cdecl; external ImguiLibName; +procedure ImDrawList_PrimRectUV(list: PImDrawList; a: ImVec2; b: ImVec2; uv_a: ImVec2; uv_b: ImVec2; col: ImU32); cdecl; external ImguiLibName; +procedure ImDrawList_PrimQuadUV(list: PImDrawList; a: ImVec2; b: ImVec2; c: ImVec2; d: ImVec2; uv_a: ImVec2; uv_b: ImVec2; uv_c: ImVec2; + uv_d: ImVec2; col: ImU32); cdecl; external ImguiLibName; +procedure ImDrawList_PrimWriteVtx(list: PImDrawList; pos: ImVec2; uv: ImVec2; col: ImU32); cdecl; external ImguiLibName; +procedure ImDrawList_PrimWriteIdx(list: PImDrawList; idx: ImDrawIdx); cdecl; external ImguiLibName; +procedure ImDrawList_PrimVtx(list: PImDrawList; pos: ImVec2; uv: ImVec2; col: ImU32); cdecl; external ImguiLibName; +procedure ImDrawList_UpdateClipRect(list: PImDrawList); cdecl; external ImguiLibName; +procedure ImDrawList_UpdateTextureID(list: PImDrawList); cdecl; external ImguiLibName; + + +//binding helpers +function ImVec2Init(const x, y: single): Imvec2; inline; + +implementation + +function ImVec2Init(const x, y: single): Imvec2; +begin + result.x := x; + result.y := y; +end; + +end. diff --git a/model_viewer/imgui/imgui_impl_sdlgl2.pas b/model_viewer/imgui/imgui_impl_sdlgl2.pas new file mode 100644 index 0000000..c262862 --- /dev/null +++ b/model_viewer/imgui/imgui_impl_sdlgl2.pas @@ -0,0 +1,300 @@ +{ +Translation of "ImGui SDL2 binding with OpenGL" example, using SDL2 headers provided by https://github.com/ev1313/Pascal-SDL-2-Headers + +In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. + +You can copy and use unmodified imgui_impl_* files in your project. +If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), Imgui_ImplSdlGL2_RenderDrawLists() and ImGui_ImplXXXX_Shutdown(). +If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. +https://github.com/ocornut/imgui +} + +unit imgui_impl_sdlgl2; +{$mode objfpc}{$H+} + +interface + +uses + sdl2, gl, glu, GLext, + imgui; + +procedure ImGui_ImplSdlGL2_Init(); +procedure ImGui_ImplSdlGL2_Shutdown(); +procedure ImGui_ImplSdlGL2_NewFrame(window: PSDL_Window); +procedure Imgui_ImplSdlGL2_RenderDrawLists(draw_data: PImDrawData); cdecl; +function ImGui_ImplSdlGL2_ProcessEvent(event: PSDL_Event): boolean; + +implementation + +// Data +var + g_Time: double = 0.0; + g_MousePressed: array[0..2] of bool = ( false, false, false ); + g_MouseWheel: single = 0.0; + g_FontTexture: GLuint = 0; + +function ImGui_ImplSdlGL2_ProcessEvent(event: PSDL_Event): boolean; +var + key: TSDL_KeyCode; + io: PImGuiIO; +begin + result := false; + io := igGetIO(); + case event^.type_ of + SDL_MOUSEWHEEL: begin + if (event^.wheel.y > 0) then + g_MouseWheel := 1; + if (event^.wheel.y < 0) then + g_MouseWheel := -1; + result := true; + end; + SDL_MOUSEBUTTONDOWN: begin + if (event^.button.button = SDL_BUTTON_LEFT) then g_MousePressed[0] := true; + if (event^.button.button = SDL_BUTTON_RIGHT) then g_MousePressed[1] := true; + if (event^.button.button = SDL_BUTTON_MIDDLE) then g_MousePressed[2] := true; + result := true; + end; + SDL_TEXTINPUT: begin + ImGuiIO_AddInputCharactersUTF8(event^.text.text); + result := true; + end; + SDL_KEYDOWN, SDL_KEYUP: begin + key := event^.key.keysym.sym and (not SDLK_SCANCODE_MASK); + io^.KeysDown[key] := event^.type_ = SDL_KEYDOWN; + io^.KeyShift := (SDL_GetModState() and KMOD_SHIFT) <> 0; + io^.KeyCtrl := (SDL_GetModState() and KMOD_CTRL) <> 0; + io^.KeyAlt := (SDL_GetModState() and KMOD_ALT) <> 0; + io^.KeySuper := (SDL_GetModState() and KMOD_GUI) <> 0; + result := true; + end; + end; +end; + + +procedure ImGui_ImplSdlGL2_CreateDeviceObjects(); +var + io: PImGuiIO; + pixels: pbyte; + width, height: integer; + font_atlas: PImFontAtlas; + last_texture: GLint; +begin + // Build texture atlas + io := igGetIO(); + font_atlas := io^.Fonts; + //ImFontAtlas_AddFontDefault(font_atlas); + ImFontAtlas_GetTexDataAsAlpha8(font_atlas, @pixels, @width, @height); + + // Upload texture to graphics system + glGetIntegerv(GL_TEXTURE_BINDING_2D, @last_texture); + glGenTextures(1, @g_FontTexture); + glBindTexture(GL_TEXTURE_2D, g_FontTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels); + + // Store our identifier + ImFontAtlas_SetTexID(font_atlas, Pointer(g_FontTexture)); + + // Restore state + glBindTexture(GL_TEXTURE_2D, last_texture); +end; + +procedure ImGui_ImplSdlGL2_InvalidateDeviceObjects(); +begin + if (g_FontTexture <> 0) then begin + glDeleteTextures(1, @g_FontTexture); + ImFontAtlas_SetTexID(igGetIO()^.Fonts, nil); + g_FontTexture := 0; + end; +end; + + +procedure ImGui_ImplSdlGL2_Init(); +var + io: PImGuiIO; +begin + Load_GL_version_2_0; + io := igGetIO(); + + // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. + io^.KeyMap[ImGuiKey_Tab] := SDLK_TAB; + io^.KeyMap[ImGuiKey_LeftArrow] := SDL_SCANCODE_LEFT; + io^.KeyMap[ImGuiKey_RightArrow] := SDL_SCANCODE_RIGHT; + io^.KeyMap[ImGuiKey_UpArrow] := SDL_SCANCODE_UP; + io^.KeyMap[ImGuiKey_DownArrow] := SDL_SCANCODE_DOWN; + io^.KeyMap[ImGuiKey_PageUp] := SDL_SCANCODE_PAGEUP; + io^.KeyMap[ImGuiKey_PageDown] := SDL_SCANCODE_PAGEDOWN; + io^.KeyMap[ImGuiKey_Home] := SDL_SCANCODE_HOME; + io^.KeyMap[ImGuiKey_End] := SDL_SCANCODE_END; + io^.KeyMap[ImGuiKey_Delete] := SDLK_DELETE; + io^.KeyMap[ImGuiKey_Backspace] := SDLK_BACKSPACE; + io^.KeyMap[ImGuiKey_Enter] := SDLK_RETURN; + io^.KeyMap[ImGuiKey_Escape] := SDLK_ESCAPE; + io^.KeyMap[ImGuiKey_A] := SDLK_a; + io^.KeyMap[ImGuiKey_C] := SDLK_c; + io^.KeyMap[ImGuiKey_V] := SDLK_v; + io^.KeyMap[ImGuiKey_X] := SDLK_x; + io^.KeyMap[ImGuiKey_Y] := SDLK_y; + io^.KeyMap[ImGuiKey_Z] := SDLK_z; + + io^.RenderDrawListsFn := @Imgui_ImplSdlGL2_RenderDrawLists; //todo assign + io^.SetClipboardTextFn := nil; + io^.GetClipboardTextFn := nil; + io^.ClipboardUserData := nil; +end; + +procedure ImGui_ImplSdlGL2_Shutdown(); +begin + ImGui_ImplSdlGL2_InvalidateDeviceObjects(); + igShutdown(); +end; + +procedure ImGui_ImplSdlGL2_NewFrame(window: PSDL_Window); +var + w, h: integer; + display_w, display_h: integer; + io: PImGuiIO; + time, mouseMask: UInt32; + current_time: double; + mx, my: Integer; +begin + if g_FontTexture = 0 then + ImGui_ImplSdlGL2_CreateDeviceObjects(); + + io := igGetIO(); + + // Setup display size (every frame to accommodate for window resizing) + SDL_GetWindowSize(window, @w, @h); + io^.DisplaySize := ImVec2Init(w, h); + io^.DisplayFramebufferScale := ImVec2Init(1, 1); + + // SDL_GL_GetDrawableSize might be missing in pascal sdl2 headers - remove the next 3 lines in that case + SDL_GL_GetDrawableSize(window, @display_w, @display_h); + if (w <> 0) and (h <> 0) and ((w <> display_w) or (h <> display_h)) then + io^.DisplayFramebufferScale := ImVec2Init(display_w/w, display_h/h); + + // Setup time step + time := SDL_GetTicks(); + current_time := time / 1000.0; + if (g_Time > 0.0) then + io^.DeltaTime := current_time - g_Time + else + io^.DeltaTime := 1.0/60.0; + g_Time := current_time; + + // Setup inputs + // (we already got mouse wheel, keyboard keys & characters from SDL_PollEvent()) + mouseMask := SDL_GetMouseState(@mx, @my); + if ((SDL_GetWindowFlags(window) and SDL_WINDOW_MOUSE_FOCUS) <> 0) then + io^.MousePos := ImVec2Init(mx, my) // Mouse position, in pixels (set to -1,-1 if no mouse / on another screen, etc.) + else + io^.MousePos := ImVec2Init(-1,-1); + + // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. + io^.MouseDown[0] := g_MousePressed[0] or (mouseMask and SDL_BUTTON(SDL_BUTTON_LEFT) <> 0); + io^.MouseDown[1] := g_MousePressed[1] or (mouseMask and SDL_BUTTON(SDL_BUTTON_RIGHT) <> 0); + io^.MouseDown[2] := g_MousePressed[2] or (mouseMask and SDL_BUTTON(SDL_BUTTON_MIDDLE) <> 0); + g_MousePressed[0] := false; + g_MousePressed[1] := false; + g_MousePressed[2] := false; + + io^.MouseWheel := g_MouseWheel; + g_MouseWheel := 0.0; + + // Hide OS mouse cursor if ImGui is drawing it + //SDL_ShowCursor(io.MouseDrawCursor ? 0 : 1); + + // Start the frame + igNewFrame(); +end; + +procedure Imgui_ImplSdlGL2_RenderDrawLists(draw_data: PImDrawData); cdecl; +var + last_texture: GLint; + last_viewport: array[0..3] of GLint; + last_scissor_box: array[0..3] of GLint; + io: PImGuiIO; + fb_width, fb_height, n, cmd_i: integer; + cmd_list: PImDrawList; + vtx_buffer: PImDrawVert; + idx_buffer: PImDrawIdx; + pcmd: PImDrawCmd; +begin + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + io := igGetIO(); + fb_width := trunc(io^.DisplaySize.x * io^.DisplayFramebufferScale.x); + fb_height := trunc(io^.DisplaySize.y * io^.DisplayFramebufferScale.y); + if (fb_width = 0) or (fb_height = 0) then + exit; + //draw_data->ScaleClipRects(io.DisplayFramebufferScale); + + glGetIntegerv(GL_TEXTURE_BINDING_2D, @last_texture); + glGetIntegerv(GL_VIEWPORT, @last_viewport); + glGetIntegerv(GL_SCISSOR_BOX, @last_scissor_box); + glPushAttrib(GL_ENABLE_BIT or GL_COLOR_BUFFER_BIT or GL_TRANSFORM_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnable(GL_TEXTURE_2D); + //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context + + // Setup viewport, orthographic projection matrix + glViewport(0, 0, fb_width, fb_height); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0.0, io^.DisplaySize.x, io^.DisplaySize.y, 0.0, -1.0, +1.0); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + // Render command lists + Assert(SizeOf(ImDrawIdx) = 2); + + for n := 0 to draw_data^.CmdListsCount - 1 do begin + cmd_list := draw_data^.CmdLists[n]; + vtx_buffer := cmd_list^.VtxBuffer.Data; + idx_buffer := cmd_list^.IdxBuffer.Data; + + //pos/uv/color offsets: 0, 8, 16 + glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), Pbyte(vtx_buffer) + 0); + glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), Pbyte(vtx_buffer) + 8); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), Pbyte(vtx_buffer) + 16); + + for cmd_i := 0 to cmd_list^.CmdBuffer.Size - 1 do begin + pcmd := @(cmd_list^.CmdBuffer.Data[cmd_i]); + if pcmd^.UserCallback <> nil then begin + pcmd^.UserCallback(cmd_list, pcmd); + end else begin + glBindTexture(GL_TEXTURE_2D, GLuint(pcmd^.TextureId)); + glScissor(trunc(pcmd^.ClipRect.x), trunc(fb_height - pcmd^.ClipRect.w), + trunc(pcmd^.ClipRect.z - pcmd^.ClipRect.x), trunc(pcmd^.ClipRect.w - pcmd^.ClipRect.y)); + glDrawElements(GL_TRIANGLES, pcmd^.ElemCount, GL_UNSIGNED_SHORT, idx_buffer); + end; + idx_buffer += pcmd^.ElemCount + end; + end; + + // Restore modified state + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glBindTexture(GL_TEXTURE_2D, last_texture); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glPopAttrib(); + glViewport(last_viewport[0], last_viewport[1], GLsizei(last_viewport[2]), GLsizei(last_viewport[3])); + glScissor(last_scissor_box[0], last_scissor_box[1], GLsizei(last_scissor_box[2]), GLsizei(last_scissor_box[3])); +end; + +end. + diff --git a/model_viewer/model_viewer.lpi b/model_viewer/model_viewer.lpi new file mode 100644 index 0000000..ff9130e --- /dev/null +++ b/model_viewer/model_viewer.lpi @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + <CommandLineParams Value="vwing.hob"/> + </local> + </RunParams> + <RequiredPackages Count="1"> + <Item1> + <PackageName Value="LCL"/> + </Item1> + </RequiredPackages> + <Units Count="8"> + <Unit0> + <Filename Value="model_viewer.pas"/> + <IsPartOfProject Value="True"/> + </Unit0> + <Unit1> + <Filename Value="hob_mesh.pas"/> + <IsPartOfProject Value="True"/> + </Unit1> + <Unit2> + <Filename Value="..\rs_units\rs_image.pas"/> + <IsPartOfProject Value="True"/> + </Unit2> + <Unit3> + <Filename Value="..\rs_units\hmt_parser.pas"/> + <IsPartOfProject Value="True"/> + </Unit3> + <Unit4> + <Filename Value="..\rs_units\hob_parser.pas"/> + <IsPartOfProject Value="True"/> + </Unit4> + <Unit5> + <Filename Value="sdl2\sdl2.pas"/> + <IsPartOfProject Value="True"/> + </Unit5> + <Unit6> + <Filename Value="imgui\imgui.pas"/> + <IsPartOfProject Value="True"/> + </Unit6> + <Unit7> + <Filename Value="imgui\imgui_impl_sdlgl2.pas"/> + <IsPartOfProject Value="True"/> + </Unit7> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="model_viewer"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="..\rs_units;sdl2;imgui"/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + </CompilerOptions> + <Debugging> + <Exceptions Count="2"> + <Item1> + <Name Value="ECodetoolError"/> + </Item1> + <Item2> + <Name Value="EFOpenError"/> + </Item2> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/model_viewer/model_viewer.pas b/model_viewer/model_viewer.pas new file mode 100644 index 0000000..06587bf --- /dev/null +++ b/model_viewer/model_viewer.pas @@ -0,0 +1,431 @@ +(* +Copyright (c) 2017 David Pethes + +This file is part of RS model viewer. + +RS model viewer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +RS model viewer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RS model viewer. If not, see <http://www.gnu.org/licenses/>. +*) +program model_viewer; + +uses + sysutils, math, + gl, glu, glext, sdl2, imgui, imgui_impl_sdlgl2, + hob_mesh; + +const + SCR_W_fscrn = 1024; + SCR_H_fscrn = 768; + SCR_W_INIT = 1280; + SCR_H_INIT = 720; + SCREEN_BPP = 0; + RotationAngleIncrement = 1; + ZoomIncrement = 0.3; + MouseZoomDistanceMultiply = 0.15; + PitchIncrement = 0.5; + MouseTranslateMultiply = 0.025; + +var + g_window: PSDL_Window; + g_ogl_context: TSDL_GLContext; + + model: TModel; + + view: record + rotation_angle: single; + distance: single; + pitch: single; + x, y: single; + autorotate: boolean; + opts: TRenderOpts; + end; + + key_pressed: record + wireframe: boolean; + vcolors: boolean; + points: boolean; + textures: boolean; + fullscreen: boolean; + autorotate: boolean; + fg: boolean; + end; + + mouse: record + drag: boolean; + translate: boolean; + last_x, last_y: integer; + resume_autorotate_on_release: boolean; + end; + +procedure AppError(s: string); +begin + writeln(stderr, s); + halt; +end; + + +// initial parameters +procedure InitGL; +var + ogl_info: string; +begin + ogl_info := format('vendor: %s renderer: %s', [glGetString(GL_VENDOR), glGetString(GL_RENDERER)]); + writeln(ogl_info); + ogl_info := 'version: ' + glGetString(GL_VERSION); + writeln(ogl_info); + + //glShadeModel( GL_SMOOTH ); // Enable smooth shading + glClearColor( 0.0, 0.0, 0.0, 0.0 ); + glClearDepth( 1.0 ); // Depth buffer setup + glEnable( GL_DEPTH_TEST ); // Enables Depth Testing + glDepthFunc( GL_LEQUAL ); // The Type Of Depth Test To Do + glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); // Really Nice Perspective Calculations + + //glEnable( GL_CULL_FACE ); //backface culling + //glCullFace( GL_BACK ); + + glEnable(GL_TEXTURE_2D); +end; + + +// function to reset our viewport after a window resize +procedure SetGLWindowSize( width, height : integer ); +begin + if ( height = 0 ) then + height := 1; // Protect against a divide by zero + + glViewport( 0, 0, width, height ); // Setup our viewport. + glMatrixMode( GL_PROJECTION ); // change to the projection matrix and set our viewing volume. + glLoadIdentity; + gluPerspective(45.0, width / height, 0.1, 100.0); // Set our perspective + // glOrtho( 0, width, height, 0, - 1, 1); + glMatrixMode( GL_MODELVIEW ); // Make sure we're changing the model view and not the projection + glLoadIdentity; // Reset The View +end; + + +// The main drawing function. +procedure DrawGLScene; +begin + glClear( GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT ); + glLoadIdentity; + + if view.distance < ZoomIncrement then + view.distance := ZoomIncrement; + + glTranslatef(view.x, view.y, -view.distance); + glRotatef(view.rotation_angle, 0.0, 1.0, 0.0); + glRotatef(view.pitch, 1, 0, 0); + + if view.autorotate then + view.rotation_angle += RotationAngleIncrement; + if view.rotation_angle > 360 then + view.rotation_angle -= 360; + + model.DrawGL(view.opts); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + igRender; + + SDL_GL_SwapWindow(g_window); +end; + + +procedure WindowInit(w_width, w_height: integer); +const + renderer_index = -1; //The index of the rendering driver to initialize: -1 to initialize the first one supporting the requested flags +var + ver: TSDL_Version; + x, y: integer; + flags: longword; + io: PImGuiIO; +begin + SDL_GetVersion(@ver); + writeln(format('SDL %d.%d.%d', [ver.major, ver.minor, ver.patch])); + //WriteVideoInfo; + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); + + WriteLn('init window: ', w_width, 'x', w_height); + x := SDL_WINDOWPOS_CENTERED; + y := SDL_WINDOWPOS_CENTERED; + flags := SDL_WINDOW_SHOWN or SDL_WINDOW_OPENGL or SDL_WINDOW_RESIZABLE; + g_window := SDL_CreateWindow('RS model viewer', x, y, w_width, w_height, flags); + if g_window = nil then + AppError ('SDL_CreateWindow failed. Reason: ' + SDL_GetError()); + + g_ogl_context := SDL_GL_CreateContext(g_window); + if g_ogl_context = nil then begin + writeln ('SDL_GL_CreateContext failed. Reason: ' + SDL_GetError()); + halt; + end; + SDL_GL_SetSwapInterval(1); //enable VSync + + //setup imgui + io := igGetIO(); + io^.DisplaySize.x := w_width; + io^.DisplaySize.y := w_height; + ImGui_ImplSdlGL2_Init(); +end; + + +procedure WindowFree; +begin + ImGui_ImplSdlGL2_Shutdown(); + SDL_GL_DeleteContext(g_ogl_context); + SDL_DestroyWindow(g_window); +end; + + +procedure WindowScreenshot(const width, height : integer); +const + head: array[0..8] of word = (0, 2, 0, 0, 0, 0, 0, 0, 24); + counter: integer = 0; +var + buf: pbyte; + f: file; + fname: string; +begin + buf := getmem(width * height * 4); + glReadBuffer(GL_FRONT); + glReadPixels(0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, buf); + + fname := format('screenshot_%.4d.tga', [counter]); + AssignFile(f, fname); + Rewrite(f, 1); + head[6] := width; + head[7] := height; + BlockWrite(f, head, sizeof(head)); + BlockWrite(f, buf^, width * height * 3); + CloseFile(f); + counter += 1; + + Freemem(buf); +end; + + +procedure InitView; +begin + view.rotation_angle := 0; + view.distance := 3; + view.pitch := 0; + view.x := 0; + view.y := 0; + view.autorotate := true; + view.opts.wireframe := false; + view.opts.points := false; + view.opts.vcolors := true; + view.opts.textures := true; +end; + + +procedure HandleEvent(const ev: TSDL_Event; var done: boolean); +var + io: PImGuiIO; +begin + ImGui_ImplSdlGL2_ProcessEvent(@ev); + io := igGetIO(); + if ((ev.type_ = SDL_MOUSEBUTTONDOWN) or + (ev.type_ = SDL_MOUSEBUTTONUP) or + (ev.type_ = SDL_MOUSEWHEEL) or + (ev.type_ = SDL_MOUSEMOTION)) and io^.WantCaptureMouse then + exit; + if ((ev.type_ = SDL_KEYDOWN) or (ev.type_ = SDL_KEYUP)) and io^.WantCaptureKeyboard then + exit; + + case ev.type_ of + SDL_QUITEV: + Done := true; + SDL_WINDOWEVENT: begin + if ev.window.event = SDL_WINDOWEVENT_RESIZED then + SetGLWindowSize(ev.window.data1, ev.window.data2); + end; + + SDL_KEYDOWN: begin + case ev.key.keysym.sym of + SDLK_ESCAPE: + Done := true; + SDLK_s: + WindowScreenshot(g_window^.w, g_window^.h); + SDLK_PAGEUP: + view.distance += ZoomIncrement; + SDLK_PAGEDOWN: + view.distance -= ZoomIncrement; + SDLK_r: + if not key_pressed.autorotate then begin + view.autorotate := not view.autorotate; + key_pressed.autorotate := true; + end; + //model rendering opts + SDLK_w: + if not key_pressed.wireframe then begin + view.opts.wireframe := not view.opts.wireframe; + key_pressed.wireframe := true; + end; + SDLK_v: + if not key_pressed.vcolors then begin + view.opts.vcolors := not view.opts.vcolors; + key_pressed.vcolors := true; + end; + SDLK_p: + if not key_pressed.points then begin + view.opts.points := not view.opts.points; + key_pressed.points := true; + end; + SDLK_t: + if not key_pressed.textures then begin + view.opts.textures := not view.opts.textures; + key_pressed.textures := true; + end; + SDLK_LEFT: + view.opts.fg_to_draw := max(0, view.opts.fg_to_draw - 1); + SDLK_RIGHT: + view.opts.fg_to_draw += 1; + end; + end; + SDL_KEYUP: begin + case ev.key.keysym.sym of + SDLK_F1: + key_pressed.fullscreen := false; + SDLK_w: + key_pressed.wireframe := false; + SDLK_v: + key_pressed.vcolors := false; + SDLK_p: + key_pressed.points := false; + SDLK_t: + key_pressed.textures := false; + SDLK_r: + key_pressed.autorotate := false; + end; + end; + + SDL_MOUSEBUTTONDOWN: begin + mouse.resume_autorotate_on_release := view.autorotate; + if ev.button.button in [1..3] then begin + mouse.drag := true; + mouse.translate := ev.button.button = 2; + mouse.last_x := ev.button.x; + mouse.last_y := ev.button.y; + view.autorotate := false; + end; + end; + SDL_MOUSEBUTTONUP: begin + mouse.drag := false; + view.autorotate := mouse.resume_autorotate_on_release; + end; + SDL_MOUSEWHEEL: begin + if ev.wheel.y < 0 then + view.distance += view.distance * MouseZoomDistanceMultiply; + if ev.wheel.y > 0 then + view.distance -= view.distance * MouseZoomDistanceMultiply; + end; + SDL_MOUSEMOTION: begin + if mouse.drag then begin + if not mouse.translate then begin + if ev.motion.y <> mouse.last_y then begin + view.pitch += PitchIncrement * ev.motion.yrel; + mouse.last_y := ev.motion.y; + end; + if ev.motion.x <> mouse.last_x then begin + view.rotation_angle += RotationAngleIncrement * ev.motion.xrel; + mouse.last_x := ev.motion.x; + end; + end else begin + if ev.motion.y <> mouse.last_y then begin + view.y -= MouseTranslateMultiply * ev.motion.yrel; + mouse.last_y := ev.motion.y; + end; + if ev.motion.x <> mouse.last_x then begin + view.x += MouseTranslateMultiply * ev.motion.xrel; + mouse.last_x := ev.motion.x; + end; + end; + end; + end; + end; {case} +end; + +//****************************************************************************** +var + sec, frames: integer; + event: TSDL_Event; + done: boolean; + hob_file, hmt_file, obj_file: string; + +begin + if Paramcount < 1 then begin + writeln('specify HOB file'); + exit; + end; + hob_file := ParamStr(1); + hmt_file := StringReplace(hob_file, '.hob', '.hmt', [rfIgnoreCase]); + model := TModel.Create; + model.Load(hob_file, hmt_file); + + writeln('Init SDL...'); + SDL_Init(SDL_INIT_VIDEO or SDL_INIT_TIMER); + WindowInit(SCR_W_INIT, SCR_H_INIT); + writeln('Init OpenGL...'); + InitGL; + SetGLWindowSize(g_window^.w, g_window^.h); + + InitView; + model.InitGL; + + //export + //obj_file := StringReplace(hob_file, '.hob', '.obj', [rfIgnoreCase]); + //model.ExportObj(obj_file); + + sec := SDL_GetTicks; + frames := 0; + Done := False; + key_pressed.wireframe := false; + key_pressed.fullscreen := false; + while not Done do begin + ImGui_ImplSdlGL2_NewFrame(g_window); + + igBegin('rendering options'); + igCheckbox('points', @view.opts.points); + igCheckbox('wireframe', @view.opts.wireframe); + igCheckbox('textures', @view.opts.textures); + igCheckbox('vertex colors', @view.opts.vcolors); + igEnd; + + DrawGLScene; + + while SDL_PollEvent(@event) > 0 do + HandleEvent(event, done); + + frames += 1; + if (SDL_GetTicks - sec) >= 1000 then begin + write(frames:3, ' dist: ', view.distance:5:1, ' rot: ', view.rotation_angle:5:1, #13); + frames := 0; + sec := SDL_GetTicks; + end; + SDL_Delay(10); + //WindowScreenshot( surface^.w, surface^.h ); + end; + + model.Free; + + WindowFree; + SDL_Quit; +end. + diff --git a/rs_units/hmt_parser.pas b/rs_units/hmt_parser.pas new file mode 100644 index 0000000..4890e84 --- /dev/null +++ b/rs_units/hmt_parser.pas @@ -0,0 +1,183 @@ +unit hmt_parser; +{$mode objfpc}{$H+} + +interface + +uses + sysutils, Classes, + rs_image; + +type + THmtMaterial = record + type_: shortint; + tex_index: shortint; + unknown_float1, unknown_float2: single; + zero: integer; + hex_a: integer; + name: array[0..15] of byte; + name_string: string; + end; + + THmtTexture = record + data_offset: integer; + palette_offset: integer; + name_offset: integer; + width, height: word; + name: array[0..15] of byte; + name_string: string; + image: TRSImage; + end; + + THmtFile = record + material_count: integer; + texture_offset: integer; + texture_count: integer; + materials: array of THmtMaterial; + textures: array of THmtTexture; + end; + + function ParseHmtFile(const fname: string): THmtFile; + +//************************************************************************************************** +implementation + +function NameToString(name: array of byte): string; +var + i: Integer; +begin + result := ''; + for i := 0 to length(name) - 1 do begin + if name[i] = 0 then break; + result += char( name[i] ); + end; +end; + + +procedure ReadTexture(var tex: THmtTexture; var f: TMemoryStream); +const + ImageDescription: array[0..5] of TImageDescription = ( + (palette_entries: 16; sample_bits: 4), + (palette_entries: 256; sample_bits: 8), + (palette_entries: 0; sample_bits: 16), + (palette_entries: 0; sample_bits: 32), + (palette_entries: 0; sample_bits: 4), + (palette_entries: 0; sample_bits: 16) + ); +var + image: TRSImage; + buf: array[0..27] of byte; + description: TImageDescription; + bpp: byte; + color_rgba: integer; + pos: int64; +begin + tex.data_offset := f.ReadDWord; + f.ReadBuffer(buf, 28); + tex.palette_offset := f.ReadDWord; + tex.name_offset := f.ReadDWord; + tex.width := f.ReadWord; + tex.height := f.ReadWord; + + f.ReadByte; //0x01 + bpp := f.ReadByte; + image.type_ := f.ReadByte; + f.ReadByte; + color_rgba := f.ReadDWord; + + pos := f.Position; + f.Seek(tex.name_offset, TSeekOrigin.soBeginning); + f.ReadBuffer(tex.name, 16); + tex.name_string := NameToString(tex.name); + f.Seek(pos, TSeekOrigin.soBeginning); + + description := ImageDescription[image.type_]; + image.sampleBits := description.sample_bits; + image.paletteEntries := description.palette_entries; + image.width := tex.width; + //if (image.width and 1) > 1 then image.width += 1; + image.height := tex.height; + + writeln('name: ', tex.name_string); + writeln('size: ', tex.width, 'x', tex.height); + writeln('subtype: ', image.type_, ' bpp: ', bpp); + writeln('sample bits: ', image.sampleBits); + writeln('palette offset: ', tex.palette_offset); + writeln('data offset: ', tex.data_offset); + + if tex.palette_offset > 0 then begin + writeln('palette entries: ', image.paletteEntries); + f.Seek(tex.palette_offset, TSeekOrigin.soBeginning); + LoadPalette(image, f); + end; + f.Seek(tex.data_offset, TSeekOrigin.soBeginning); + LoadSamples(image, f); + DecodePixels(image); + + f.Seek(pos, TSeekOrigin.soBeginning); + writeln; + tex.image := image; +end; + + +procedure ReadMaterial(var mat: THmtMaterial; var f: TMemoryStream); +begin + mat.type_ := f.ReadWord; + mat.tex_index := f.ReadWord; + mat.unknown_float1 := f.ReadDWord; + mat.unknown_float2 := f.ReadDWord; + mat.zero := f.ReadDWord; + mat.hex_a := f.ReadDWord; + f.ReadBuffer(mat.name, 16); + mat.name_string := NameToString(mat.name); + + writeln(mat.name_string); + if (mat.zero <> 0) or (mat.hex_a <> $A) then + writeln('unusual file'); + writeln(' type1: ', mat.type_); + writeln(' type2: ', mat.tex_index); +end; + + +function ParseHmtFile(const fname: string): THmtFile; +var + f: TMemoryStream; + hmt: THmtFile; + i: Integer; +begin + f := TMemoryStream.Create; + f.LoadFromFile(fname); + + //read main info + hmt.material_count := f.ReadDWord; + hmt.texture_offset := f.ReadDWord; + f.Seek(hmt.texture_offset, TSeekOrigin.soBeginning); + hmt.texture_count := f.ReadDWord; + f.Seek(8, TSeekOrigin.soBeginning); + + //read materials + writeln('materials: ', hmt.material_count); + SetLength(hmt.materials, hmt.material_count); + for i := 0 to hmt.material_count - 1 do begin + ReadMaterial(hmt.materials[i], f); + end; + + if hmt.texture_count = 0 then begin + result := hmt; + f.Free; + exit; + end; + + //read textures + writeln('textures: ', hmt.texture_count); + f.Seek(hmt.texture_offset + 4, TSeekOrigin.soBeginning); + SetLength(hmt.textures, hmt.texture_count); + for i := 0 to hmt.texture_count - 1 do begin + ReadTexture(hmt.textures[i], f); + end; + + f.Free; + result := hmt; +end; + +end. + diff --git a/rs_units/hob_parser.pas b/rs_units/hob_parser.pas new file mode 100644 index 0000000..6d34f4f --- /dev/null +++ b/rs_units/hob_parser.pas @@ -0,0 +1,361 @@ +unit hob_parser; +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils; + +type + TRGBA = record + color: integer; + end; + + TTexCoord = record + u, v: smallint; + end; + + THobFace = record + flags: integer; + b1, b2, b3: byte; + bsize: byte; + ftype: byte; //3 - tri, 4 - quad + material_index: word; + indices: array[0..3] of word; + vertex_colors: array[0..3] of TRGBA; + tex_coords: array[0..3] of TTexCoord; + end; + + THobFaceGroup = record + meshdef1_offset: integer; + + face_block_end_offset, + face_block_offset, + vertex_block_offset: integer; + + fg_group_id: integer; + transform: record + x,y,z: single; + end; + + face_count: integer; + faces: array of THobFace; + + vertex_count: integer; + vertices: array of record + x, y, z, unknown: smallint; //+-2^15 + end; + end; + + THobObject = record + name: array[0..15] of byte; + face_group_offset: integer; + face_group_header_offset: integer; + face_group_header2_offset: integer; + + face_group_count: integer; + face_group_count0: integer; + + face_groups: array of THobFaceGroup; + end; + + THobFile = record + obj_count: integer; + objects: array of THobObject; + end; + +function ParseHobFile(const fname: string): THobFile; + +//************************************************************************************************** +implementation + +const + DumpFaces = false; + +function NameToString(name: array of byte): string; +var + i: Integer; +begin + result := ''; + for i := 0 to length(name) - 1 do begin + if name[i] = 0 then break; + result += char( name[i] ); + end; +end; + +procedure ReadFaces(var group: THobFaceGroup; var f: TMemoryStream); +const + FACE_UV = %100; + FACE_QUAD = %1000; + FACE_VCOLORS = %10000; + FACE_COLOR = %100000; + FACE_EXT = %1000000; +var + i, k: integer; + face: THobFace; + zero: integer; + file_pos: integer; + color: integer; +begin + zero := f.ReadDWord; + if (zero <> 0) then + writeln('unusual file: zero'); + zero := f.ReadDWord; + if (zero <> 0) then + writeln('unusual file: zero'); + file_pos := f.ReadDWord; + if file_pos <> f.Position + 4 then + writeln('unusual file: face data start position'); + group.face_count := f.ReadDWord; + writeln('faces: ', group.face_count); + + SetLength(group.faces, group.face_count); + for i := 0 to group.face_count - 1 do begin + file_pos := f.Position; + face.flags := f.ReadDWord; + face.b1 := f.ReadByte; //46/49/4B + face.b2 := f.ReadByte; //51/71 + face.b3 := f.ReadByte; //0C + face.bsize := f.ReadByte * 4; //block size + zero := f.ReadWord; + if (zero <> 0) then + writeln('unusual file: face header separator'); + + //material index + face.material_index := f.ReadWord; + + //face type: quad or triangle + if face.flags and FACE_QUAD > 0 then + face.ftype := 4 + else + face.ftype := 3; + + //read vertex indices + for k := 0 to 3 do + face.indices[k] := f.ReadWord; + + //ext0 + if face.flags and FACE_EXT > 0 then begin + f.ReadDWord; + f.ReadDWord; + end; + + //vertex colors - either per vertex, or the same for all vertices + if face.flags and FACE_COLOR > 0 then begin + if face.flags and FACE_VCOLORS > 0 then begin + for k := 0 to face.ftype - 1 do + face.vertex_colors[k].color := f.ReadDWord; + end else begin + color := f.ReadDWord; + for k := 0 to face.ftype - 1 do + face.vertex_colors[k].color := color; + end; + end; + + //uv coords + if face.flags and FACE_UV > 0 then begin + for k := 0 to face.ftype - 1 do begin + face.tex_coords[k].u := f.ReadWord; + face.tex_coords[k].v := f.ReadWord; + end; + end; + + if DumpFaces then begin + if (face.flags and FACE_QUAD) = 0 then write('t') else write('q'); + write(face.flags:5, face.b1:3, face.b2:4, face.b3:3, face.bsize:3); + write(' mt: ', face.material_index); + write(' verts: '); + for k := 0 to face.ftype - 1 do + write(face.indices[k]:4); + write(' colors: '); + for k := 0 to face.ftype - 1 do + write(IntToHex(face.vertex_colors[k].color, 8), ' '); + if (face.flags and FACE_UV) > 0 then begin + write(' uvs: '); + for k := 0 to face.ftype - 1 do + write('(', face.tex_coords[k].u, ', ', face.tex_coords[k].v, ') '); + end; + end; + + //hack for awing.hob + if f.Position <> (file_pos + face.bsize) then begin + write(' rest:'); + for k := f.Position to file_pos + face.bsize - 1 do + write(IntToHex(f.ReadByte, 2):3); + end; + + if DumpFaces then + writeln; + + group.faces[i] := face; + end; +end; + + +procedure ReadVertices(var group: THobFaceGroup; var f: TMemoryStream; const vertex_count: integer); +var + i: integer; +begin + SetLength(group.vertices, vertex_count); + for i := 0 to vertex_count - 1 do begin + group.vertices[i].x := SmallInt(f.ReadWord); + group.vertices[i].y := SmallInt(f.ReadWord); + group.vertices[i].z := SmallInt(f.ReadWord); + group.vertices[i].unknown := SmallInt(f.ReadWord); + end; +end; + +var fgid: integer = 0; + +procedure ReadFaceGroup(var fg: THobFaceGroup; var f: TMemoryStream); +var + filepos: int64; + fnum: single; + i: Integer; + zero: int64; + fg_next, fg_end: integer; +begin + //save file position before seeking to face/vertex data and restore it, to read next group properly + filepos := f.Position; + + //read group/meshdef0 + fg_next := f.ReadDWord; + f.Seek(4*2, fsFromCurrent); //unknown + fg_end := f.ReadDWord; + fg.meshdef1_offset := f.ReadDWord; + + writeln(); + writeln('fg: ', fgid); fgid += 1; + writeln('fg next: ', fg_next, ' end: ', fg_end); + writeln('fg meshdef1 offset:', fg.meshdef1_offset); + + zero := f.ReadQWord; + if zero <> 0 then + writeln('unusual file: facegroup 0 zero'); + + for i := 1 to (48) div 4 do begin + f.ReadBuffer(fnum, 4); + //writeln(fnum); + end; + fg.fg_group_id := f.ReadDWord; + for i := 1 to (3*4 + 3*4 + 4*4) div 4 do begin + f.ReadBuffer(fnum, 4); + //writeln(fnum); + end; + f.ReadBuffer(fg.transform.x, 4); + f.ReadBuffer(fg.transform.y, 4); + f.ReadBuffer(fg.transform.z, 4); + + writeln(fg.fg_group_id); + writeln(fg.transform.x:7:5); + writeln(fg.transform.y:7:5); + writeln(fg.transform.z:7:5); + + if fg.meshdef1_offset > 0 then begin + //read meshdef1 + f.Seek(fg.meshdef1_offset - 4, fsFromBeginning); + fg.face_block_end_offset := f.ReadDWord; + f.Seek(20, fsFromCurrent); //zero + fg.vertex_count := f.ReadDWord; + f.Seek(8, fsFromCurrent); //zero + fg.face_block_offset := f.ReadDWord; + fg.vertex_block_offset := f.ReadDWord; + + //faces + writeln('faces at: ', fg.face_block_offset, hexStr(fg.face_block_offset, 4):6); + f.Seek(fg.face_block_offset, fsFromBeginning); + ReadFaces(fg, f); + + //vertices + writeln('vertices at: ', fg.vertex_block_offset, hexStr(fg.vertex_block_offset, 4):6); + f.Seek(fg.vertex_block_offset, fsFromBeginning); + ReadVertices(fg, f, fg.vertex_count); + + //if (scale > 0) then + //for i := 0 to fg.vertex_count - 1 do begin + // //fg.vertices[i].x += trunc(tx * 300); + // //fg.vertices[i].y += trunc(ty * 300); + // //fg.vertices[i].z += trunc(tz * 300); + // fg.vertices[i].x *= scale; + // fg.vertices[i].y *= scale; + // fg.vertices[i].z *= scale; + //end; + end; +end; + + +procedure ReadObject(var mesh: THobObject; var f: TMemoryStream); +var + i: integer; + fg_offsets: array of integer; + unknown: integer; +begin + f.ReadBuffer(mesh.name, 16); + mesh.face_group_offset := f.ReadDWord; + mesh.face_group_header_offset := f.ReadDWord; + mesh.face_group_header2_offset := f.ReadDWord; + + writeln('object: ', NameToString(mesh.name)); + writeln('face group offset: ', mesh.face_group_offset); + + //get face group count + f.Seek(mesh.face_group_header_offset, fsFromBeginning); //16B zero + mesh.face_group_count := f.ReadWord; //which? + mesh.face_group_count0 := f.ReadWord; + if mesh.face_group_count <> mesh.face_group_count0 then begin + writeln('facegroup counts don''t match!: ', mesh.face_group_count, mesh.face_group_count0:5); + end; + + SetLength(fg_offsets, mesh.face_group_count); + for i := 0 to mesh.face_group_count - 1 do begin + unknown := f.ReadDWord; + fg_offsets[i] := f.ReadDWord; + end; + + //read face group defs + SetLength(mesh.face_groups, mesh.face_group_count); + for i := 0 to mesh.face_group_count - 1 do begin + writeln('fg meshdef0 offset: ', fg_offsets[i], IntToHex(fg_offsets[i], 8):9); + f.Seek(fg_offsets[i], fsFromBeginning); + ReadFaceGroup(mesh.face_groups[i], f); + end; + writeln; +end; + + +function ParseHobFile(const fname: string): THobFile; +var + f: TMemoryStream; + hob: THobFile; + i: integer; + filepos: int64; +begin + f := TMemoryStream.Create; + f.LoadFromFile(fname); + + hob.obj_count := f.ReadDWord; + f.ReadDWord; //sometimes face block start, but useless in general + + writeln('objects: ', hob.obj_count); + if hob.obj_count = 0 then begin + writeln('hob file is empty!'); + result := hob; + exit; + end; + + SetLength(hob.objects, hob.obj_count); + for i := 0 to hob.obj_count - 1 do begin + filepos := f.Position; + ReadObject(hob.objects[i], f); + + //seek to next object header + if i + 1 < hob.obj_count then + f.Seek(filepos + 116, fsFromBeginning); + end; + + f.Free; + result := hob; +end; + +end. + diff --git a/rs_units/rs_image.pas b/rs_units/rs_image.pas new file mode 100644 index 0000000..67ea886 --- /dev/null +++ b/rs_units/rs_image.pas @@ -0,0 +1,178 @@ +unit rs_image; +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils; + +type + TRGB = array[0..2] of byte; + PRGB = ^TRGB; + TPalette = array[0..255] of TRGB; + + TRSImage = record + data_size: integer; + width, height: integer; + type_: byte; + sampleBits: byte; + paletteEntries: integer; + pixels: pbyte; + samples: pbyte; + palette: TPalette; + end; + +type + TImageDescription = record + palette_entries: integer; + sample_bits: integer; + //alpha: byte; + end; + +const + ImageDescription: array[0..5] of TImageDescription = ( + (palette_entries: 16; sample_bits: 4), + (palette_entries: 256; sample_bits: 8), + (palette_entries: 0; sample_bits: 16), + (palette_entries: 0; sample_bits: 32), + (palette_entries: 0; sample_bits: 4), + (palette_entries: 0; sample_bits: 16) + ); + +procedure LoadPalette(var image: TRSImage; var f: TMemoryStream); +procedure LoadSamples(var image: TRSImage; var f: TMemoryStream); +procedure DecodePixels(var img: TRSImage); + +//************************************************************************************************** +implementation + +procedure Unpack4To8bit(const src: PByte; const samples: integer; const dst: PByte); +var + i: Integer; + v: byte; +begin + for i := 0 to samples div 2 - 1 do begin + v := src[i]; + dst[i * 2 ] := ((v shr 4) and %1111) shl 4; + dst[i * 2 + 1] := (v and %1111) shl 4; + end; +end; + + +procedure Unpack4bitTo24bitRGB(const src: PByte; const size: integer; const dst: PByte; const pal: TPalette); +var + i: Integer; + index: integer; + dest: PRGB; +begin + dest := PRGB(dst); + for i := 0 to size div 2 - 1 do begin + index := src[i]; + dest[i * 2 ] := pal[(index shr 4) and 15]; + dest[i * 2 + 1] := pal[index and 15]; + end; +end; + + +procedure Unpack8bitTo24bitRGB(const src: PByte; const size: integer; const dst: PByte; const pal: TPalette); +var + i: Integer; + index: integer; + dest: PRGB; +begin + dest := PRGB(dst); + for i := 0 to size - 1 do begin + index := src[i]; + dest[i] := pal[index]; + end; +end; + + +procedure UseOddBytes(const src: PByte; const size: integer; const dst: pbyte); +var + i: integer; +begin + for i := 0 to size - 1 do begin + dst[i] := src[i * 2 + 1]; + end; +end; + + +procedure DecodePixels(var img: TRSImage); +var + size: integer; +begin + img.pixels := nil; + if not(img.type_ in [0, 1, 2, 3, 4, 5]) then exit; + + if img.sampleBits = 32 then begin + size := img.width * img.height * 4; + img.pixels := GetMem(size); + Move(img.samples^, img.pixels^, size); + end; + + if img.sampleBits = 4 then begin + //4bit grayscale + if img.paletteEntries = 0 then begin + size := img.width * img.height; + img.pixels := GetMem(size); + Unpack4To8bit(img.samples, size, img.pixels); + end; + //24bit RGB palettized + if img.paletteEntries = 16 then begin + size := img.width * img.height; + img.pixels := GetMem(size * 3); + Unpack4bitTo24bitRGB(img.samples, size, img.pixels, img.palette); + end; + end; + + if img.sampleBits = 8 then begin + //8bit grayscale + if img.paletteEntries = 0 then begin + size := img.width * img.height; + img.pixels := GetMem(size); + move(img.samples^, img.pixels^, size); + end; + //24bit RGB palettized + if img.paletteEntries = 256 then begin + size := img.width * img.height; + img.pixels := GetMem(size * 3); + Unpack8bitTo24bitRGB(img.samples, size, img.pixels, img.palette); + end; + end; + + if img.sampleBits = 16 then begin + size := img.width * img.height; + img.pixels := GetMem(size); + UseOddBytes(img.samples, size, img.pixels); + end; +end; + + +procedure LoadPalette(var image: TRSImage; var f: TMemoryStream); +var + entries: integer; +begin + entries := image.paletteEntries; + case entries of + 16, 256: f.ReadBuffer(image.palette, entries * 3); //RGB + end; +end; + + +procedure LoadSamples(var image: TRSImage; var f: TMemoryStream); +var + sample_bits: integer; + size: integer; +begin + sample_bits := image.sampleBits; + size := image.width * image.height * sample_bits div 8; + image.samples := getmem(size); + f.ReadBuffer(image.samples^, size); + if image.type_ = 2 then + f.ReadBuffer(image.samples^, size div 4); +end; + + +end. +