2d pixels text to 3d mesh text

Started by RemiD, November 27, 2018, 17:40:14

Previous topic - Next topic

RemiD


;see next posts for latest code

MrmediamanX

nice but couldn't one just use milkshapes text tool?
assuming the code turns text into a 3d mesh.
It's a thing that doe's when it don't..

RemiD


assuming the code turns text into a 3d mesh.

so you have not tested the code and you comment... very constructive, thanks for the useful feedback  :-*


there are many uses of such code, especially if you like to build scenes / maps proceduraly, which is my thing...
(this is a just a start, i plan to improve it, to remove notvisible tris, and to auto uvmap the resulting shape with a precise texel size)

Steve Elliott

#3
Quote
assuming the code turns text into a 3d mesh.

When you assume, you make an ass out of u and me   ;D  Well out of RemiD.
Win11 64Gb 12th Gen Intel i9 12900K 3.2Ghz Nvidia RTX 3070Ti 8Gb
Win11 16Gb 12th Gen Intel i5 12450H 2Ghz Nvidia RTX 2050 8Gb
Win11  Pro 8Gb Celeron Intel UHD Graphics 600
Win10/Linux Mint 16Gb 4th Gen Intel i5 4570 3.2GHz, Nvidia GeForce GTX 1050 2Gb
macOS 32Gb Apple M2Max
pi5 8Gb
Spectrum Next 2Mb

MrmediamanX

#4
tested though unfortunately wouldn't function ... hence presumed assumption.
according to the title.
granted for procedurally generated based maps, I can see how it could be of use.
like this one time at band camp I took a 3d maze generation code,shifted the camera to a top down
view, rotated everything to align to the zaxis/wall[now floor] and came up with a 2.5d proc gen platformer.
good times. :)


It's a thing that doe's when it don't..

RemiD

#5
Quote
tested though unfortunately wouldn't function
what is the error / problem ? (the code above does not need any external file to run, you only have to have Blitz3d installed)



For the demo i am making, i need to have an uniform texel size on all shapes / terrain, so even if there is never only one way to reach a result, i don't see myself modeling and uvmaping 26 letters, so i am going to do it in code !

MrmediamanX

#6
 my bad, it occurred when selecting the code ... one minor letter t'was all ... darn 'functio' >:(
looks good,does what's stated ... very nice. :)
It's a thing that doe's when it don't..

RemiD

Ok...

Actually it is just a start, to demonstrate how to do it for those who are interested to learn...

It is not optimised at all (notvisible tris within the mesh, not uvmapped)

RemiD

what seems like a simple thing to create (3d letters, with the same proportions than the 2d letters of a font), becomes a nightmare when you want to uvmap these 3d shapes with a uniform texel size (especially when it must be big (corresponding to 0.125unit), like what i want)

so i have done some test to modelize these 3d letters in Fragmotion, and then to uvmap them one by one, but since i want a big texel size, again i encounter this problem of cut texels... (since they are big...)

so back inside Blitz3d to try to do it all in code...

RemiD

#9
done ! 8)

so in this new version, there are no notvisible tris (within the mesh), and the shapes are uvmapped and the texel size is almost uniform (but it also depends on how the chars of the font are structured, because fitmesh() can stretch some chars if they don't fill all extremities of the average char area (example a 12x16y area for a 16font))

;2dchars (pixels) to 3dchars (meshes) by RemiD (20181201)
;this will convert the 2d chars of a font to 3d chars meshes (made of uvmaped quads)
;this can work with any font, however if you want to keep a uniform texel size,
;it is preferable to use a font in which the chars have approximately the same width,height (for example a 12x16y area for a 16font),
;and each char fills most extremities of the 12x16y area (because of the use of fitmesh() which uses the vertices at the extremities to resize the mesh)

;graphics window
Graphics3D(640,480,32,2)

HidePointer()

SeedRnd(MilliSecs())

;Camera
Global Camera = CreateCamera()
CameraViewport(Camera,0,0,GraphicsWidth(),GraphicsHeight())
CameraRange(Camera,0.15,150)
CameraClsColor(Camera,000,000,000)

;Listener
Global Listener = CreateListener(Camera)

;Input
Global MX%, MY%
Global MXDiff%, MYDiff%

;Origine
Global Origine = CreateCube()
ScaleMesh(Origine,0.01,0.01,0.01)
EntityColor(Origine,255,000,255)
EntityFX(Origine,1)

;InteractionMode
Global InteractionMode%
Const C2D% = 1
Const C3D% = 2
InteractionMode = C3D
HidePointer()

;Ghost
Global GhostRoot
Global GhostRootYaw#
Global GhostEyes
Global GhostEyesPitch#
AddGhost()

;Chars
Global CharsCount%
Dim CharStr$(100)
Dim CharPWidth%(100)
Dim CharPHeight%(100)
Dim CharImage(100)
Dim CharMesh(100)
Dim CharTexture(100)

XFont = LoadFont("Blitz",16,False,False,False)
SetFont(XFont)

AllStr$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ;"abcdefghijklmnopqrstuvwxyz" ;"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
AllWidth% = StringWidth(AllStr) : AllHeight% = StringHeight(AllStr)

For n% = 1 To Len(AllStr) Step 1
CharsCount = CharsCount + 1 : I% = CharsCount
CharStr(I) = Mid(AllStr,n,1)
CharPWidth(I) = StringWidth(CharStr(I)) : CharPHeight(I) = StringHeight(CharStr(I))
DebugLog(CharStr(I)+" "+CharPWidth(I)+","+CharPHeight(I))
CharImage(I) = CreateImage(CharPWidth(I),CharPHeight(I))
SetBuffer(ImageBuffer(CharImage(I)))
ClsColor(Rand(025,255),Rand(025,255),Rand(025,255)) : Cls()
Color(125,125,125) : Text(0,0,CharStr(I))
CharMesh(I) = CreateMesh() : CreateSurface(CharMesh(I))
CharTexture(I) = CreateTexture(2048,8)
SetBuffer(TextureBuffer(CharTexture(I)))
  ClsColor(255,255,255) : Cls()
  For nn% = 1 To 256 Step 1
   SPX% = (nn-1)*8 : SPY% = 0
   ColR% = Rand(025,225) : ColG% = Rand(025,225) : ColB% = Rand(025,225)
   For PX% = SPX To SPX+8 Step 1
    For PY% = SPY To SPY+8 Step 1
     PR% = ColR+Rand(-012,+012) : PG% = ColG+Rand(-012,+012) : PB% = ColB+Rand(-012,+012)
     Color(PR,PG,PB) : Plot(PX,PY)
    Next
   Next
  Next
EntityTexture(CharMesh(I),CharTexture(I))
Next
DebugLog(CharsCount)

PX% = 0 : PY% = 0
SetBuffer(BackBuffer())
ClsColor(000,000,000) : Cls()
For I% = 1 To CharsCount Step 1
DrawImage(CharImage(I),PX,PY)
PX% = PX+ImageWidth(CharImage(I))
Next
Flip()
;WaitKey()

;pixels list
Global PixsCount%
Dim PixPX%(16*16)
Dim PixPY%(16*16)

;premodel + preuvmap the part which will compose the chars meshes (1texel corresponds to 0.125unit)
Global XPart = CreateMesh()
Surface = CreateSurface(XPart)
AddVertex(Surface,-0.5,+0.5,0.0) : VertexTexCoords(Surface,0,Float(0)/8,Float(0)/8)
AddVertex(Surface,+0.5,+0.5,0.0) : VertexTexCoords(Surface,1,Float(8)/8,Float(0)/8)
AddVertex(Surface,-0.5,-0.5,0.0) : VertexTexCoords(Surface,2,Float(0)/8,Float(8)/8)
AddVertex(Surface,+0.5,-0.5,0.0) : VertexTexCoords(Surface,3,Float(8)/8,Float(8)/8)
AddTriangle(Surface,0,1,2)
AddTriangle(Surface,2,1,3)
UpdateNormals(XPart)
HideEntity(XPart)

OffsetX# = 0 : OffsetY# = 0
;for each char
For I% = 1 To CharsCount Step 1

PixsCount = 0
PartsCount% = 0
VerticesCount% = 0
CPX% = 0 : CPY% = 0

SetBuffer(ImageBuffer(CharImage(I)))
;for each pixel in the char image
;list the pixels composing the char
For PX% = 0 To ImageWidth(CharImage(I))-1 Step 1
  For PY% = 0 To ImageHeight(CharImage(I))-1 Step 1
   GetColor(PX,PY)
   PixRed = ColorRed() : PixGreen = ColorGreen() : PixBlue = ColorBlue()
   If( PixRed = 125 And PixGreen = 125 And PixBlue = 125 )
    PixsCount = PixsCount + 1 : II% = PixsCount
    PixPX(II) = PX : PixPY(II) = PY
   EndIf
  Next
Next
;DebugLog(PixsCount)

Surface = GetSurface(CharMesh(I),1)
;for each pixel composing the char
For II% = 1 To PixsCount Step 1
  PX% = PixPX(II) : PY% = PixPY(II)

  ;add a part at front
  PartsCount = PartsCount + 1
  TPart = CopyMesh(XPart)
  X# = PX+0.5 : Y# = ImageHeight(CharImage(I))-PY+0.5 : Z# = -0.5
  PositionMesh(TPart,X,Y,Z)
  AddMesh(TPart,CharMesh(I)) : FreeEntity(TPart)
  VerticesCount = VerticesCount + 4
  CalculatePartUVs(Surface,VerticesCount-4,VerticesCount-3,VerticesCount-2,VerticesCount-1,CPX,CPY)
  CPX = CPX + 8 : CPY = 0

  ;add a part at back
  PartsCount = PartsCount + 1
  TPart = CopyMesh(XPart)
  RotateMesh(TPart,0,180,0)
  X# = PX+0.5 : Y# = ImageHeight(CharImage(I))-PY+0.5 : Z# = +0.5
  PositionMesh(TPart,X,Y,Z)
  AddMesh(TPart,CharMesh(I)) : FreeEntity(TPart)
  VerticesCount = VerticesCount + 4
  CalculatePartUVs(Surface,VerticesCount-4,VerticesCount-3,VerticesCount-2,VerticesCount-1,CPX,CPY)
  CPX = CPX + 8 : CPY = 0

  ;check if there is a neighboor at top
  ;if no
  If( IsPix(CharImage(I),PX,PY-1,125,125,125)=False )
   ;add a part on sidetop
   PartsCount = PartsCount + 1
   TPart = CopyMesh(XPart)
   RotateMesh(TPart,+90,0,0)
   X# = PX+0.5 : Y# = ImageHeight(CharImage(I))-PY+1.0 : Z# = 0
   PositionMesh(TPart,X,Y,Z)
   AddMesh(TPart,CharMesh(I)) : FreeEntity(TPart)
   VerticesCount = VerticesCount + 4
   CalculatePartUVs(Surface,VerticesCount-4,VerticesCount-3,VerticesCount-2,VerticesCount-1,CPX,CPY)
   CPX = CPX + 8 : CPY = 0
  EndIf

  ;check if there is a neighboor at bottom
  ;if no
  If( IsPix(CharImage(I),PX,PY+1,125,125,125)=False )
   ;add a part on sidebottom
   PartsCount = PartsCount + 1
   TPart = CopyMesh(XPart)
   RotateMesh(TPart,-90,0,0)
   X# = PX+0.5 : Y# = ImageHeight(CharImage(I))-PY+0 : Z# = 0
   PositionMesh(TPart,X,Y,Z)
   AddMesh(TPart,CharMesh(I)) : FreeEntity(TPart)
   VerticesCount = VerticesCount + 4
   CalculatePartUVs(Surface,VerticesCount-4,VerticesCount-3,VerticesCount-2,VerticesCount-1,CPX,CPY)
   CPX = CPX + 8 : CPY = 0
  EndIf

  ;check if there is a neighboor at left
  ;if no
  If( IsPix(CharImage(I),PX-1,PY,125,125,125)=False )
   ;add a part on sideleft
   PartsCount = PartsCount + 1
   TPart = CopyMesh(XPart)
   RotateMesh(TPart,0,-90,0)
   X# = PX+0 : Y# = ImageHeight(CharImage(I))-PY+0.5 : Z# = 0
   PositionMesh(TPart,X,Y,Z)
   AddMesh(TPart,CharMesh(I)) : FreeEntity(TPart)
   VerticesCount = VerticesCount + 4
   CalculatePartUVs(Surface,VerticesCount-4,VerticesCount-3,VerticesCount-2,VerticesCount-1,CPX,CPY)
   CPX = CPX + 8 : CPY = 0
  EndIf
 
  ;check if there is a neighboor at right
  ;if no
  If( IsPix(CharImage(I),PX+1,PY,125,125,125)=False )
   ;add a part on sideright
   PartsCount = PartsCount + 1
   TPart = CopyMesh(XPart)
   RotateMesh(TPart,0,+90,0)
   X# = PX+1 : Y# = ImageHeight(CharImage(I))-PY+0.5 : Z# = 0
   PositionMesh(TPart,X,Y,Z)
   AddMesh(TPart,CharMesh(I)) : FreeEntity(TPart)
   VerticesCount = VerticesCount + 4
   CalculatePartUVs(Surface,VerticesCount-4,VerticesCount-3,VerticesCount-2,VerticesCount-1,CPX,CPY)
   CPX = CPX + 8 : CPY = 0
  EndIf

Next
DebugLog(PartsCount)
DebugLog(VerticesCount)
DebugLog(CountVertices(GetSurface(CharMesh(I),1))+" "+CountTriangles(GetSurface(CharMesh(I),1)))

;resize the mesh to fit in an ideal area (for a 16font this would be a 12x16y area (in 2D) and a 1.2w1.6h0.2d area (in 3D))
FitMesh(CharMesh(I),0,0,0,1.2,1.6,0.2)

PositionEntity(CharMesh(I),OffsetX,OffsetY,0,True)
RotateEntity(CharMesh(I),Rand(-12.5,+12.5),0,0,True)

;OffsetX = OffsetX + CharPWidth(I)
;OffsetY = Rand(-2,+2)

OffsetX = OffsetX + (0.1+1.2+0.1)
OffsetY = Rnd(-0.2,+0.2)

;export the char image

;export the char mesh

Next

;DebugBox = CreateCube()
;ScaleMesh(DebugBox,Float(AllWidth)/2,Float(AllHeight)/2,1.0/2)
;PositionMesh(DebugBox,Float(AllWidth)/2,Float(AllHeight)/2,0)
;EntityAlpha(DebugBox,0.1)
;EntityFX(DebugBox,1)

;for each char

;load the char image

;load the char mesh

;specify a word

;write the word using 2d letters (in a 2d world with images)

;write the word using 3d letters (in a 3d world with meshes)

;light
DLight = CreateLight(1)
LightColor(DLight,255,255,255)
PositionEntity(DLight,0,1000,-1000,True)
RotateEntity(DLight,45,0,0,True)

AmbientLight(025,025,025)

PositionEntity(GhostRoot,0,0+1.65,-3,True)
GhostRootYaw = 0 : GhostEyesPitch = 0

Global MainLoopTimer = CreateTimer(30)

Main()

End()

Function Main()

Repeat

  MainLoopMilliStart% = MilliSecs()

  MX = MouseX() : MY = MouseY()

  MXDiff = MouseXSpeed() : MYDiff = MouseYSpeed()

  UpdateInteractionMode()
  If( InteractionMode = C2D )
   ;
  Else If( InteractionMode = C3D )
   UpdateGhost()    
  EndIf    

  WireFrame(False)
  If( KeyDown(2)=1 )
   WireFrame(True)
  EndIf

  PositionEntity(Camera,EntityX(GhostEyes,True),EntityY(GhostEyes,True),EntityZ(GhostEyes,True),True)
  RotateEntity(Camera,EntityPitch(GhostEyes,True),EntityYaw(GhostEyes,True),EntityRoll(GhostEyes,True),True)

  CameraViewport(Camera,0,0,GraphicsWidth(),GraphicsHeight())
  CameraClsColor(Camera,000,000,000)
  SetBuffer(BackBuffer())
  RenderWorld()

  Color(255,255,255)
  If( KeyDown(3)=1 )
   Text(0,0,"Tris = "+TrisRendered())
   Text(0,15,"FPS = "+FPS)
  EndIf

  ;Flip(1)
  WaitTimer(MainLoopTimer)
  VWait():Flip(False)

  MainLoopMilliTime = MilliSecs() - MainLoopMilliStart
  If( MainLoopMilliTime < 1 )
   MainLoopMilliTime = 1
  EndIf

  FPS% = 1000.0/MainLoopMilliTime

Until( KeyDown(1)=1 )

End Function

Function IsPix%(Image,PX%,PY%,PR%,PG%,PB%)

SetBuffer(ImageBuffer(Image))
GetColor(PX,PY)
PixRed = ColorRed() : PixGreen = ColorGreen() : PixBlue = ColorBlue()
If( PixRed = PR And PixGreen = PG And PixBlue = PB )
  Return True
Else
  Return False
EndIf

End Function

Function CalculatePartUVs(Surface,V0I%,V1I%,V2I%,V3I%,CPX%,CPY%)

V0U# = Float(CPX+0)/2048 : V0V# = Float(CPY+0)/8 : VertexTexCoords(Surface,V0I,V0U,V0V)
V1U# = Float(CPX+8)/2048 : V1V# = Float(CPY+0)/8 : VertexTexCoords(Surface,V1I,V1U,V1V)
V2U# = Float(CPX+0)/2048 : V2V# = Float(CPY+8)/8 : VertexTexCoords(Surface,V2I,V2U,V2V)
V3U# = Float(CPX+8)/2048 : V3V# = Float(CPY+8)/8 : VertexTexCoords(Surface,V3I,V3U,V3V)

End Function

Function UpdateInteractionMode()

If( KeyHit(15)=1 )
  If( InteractionMode = C2D )
   InteractionMode = C3D
   HidePointer()
  Else If( InteractionMode = C3D )
   InteractionMode = C2D
   ShowPointer()
  EndIf
EndIf

End Function

Function AddGhost()

GhostRoot = CreatePivot()

GhostEyes = CreatePivot()

EntityParent(GhostEyes,GhostRoot,True)

End Function

Function UpdateGhost()

MoveMouse(GraphicsWidth()/2,GraphicsHeight()/2)
GhostEyesPitch = GhostEyesPitch + Float(MYDiff)/10
If( GhostEyesPitch < -89 )
  GhostEyesPitch = -89
Else If( GhostEyesPitch > 89 )
  GhostEyesPitch = 89
EndIf
RotateEntity(GhostEyes,GhostEyesPitch,0,0,False)
GhostRootYaw = GhostRootYaw - Float(MXDiff)/10
RotateEntity(GhostRoot,0,GhostRootYaw,0,False)

If( KeyDown(42) = 0 And KeyDown(29) = 0 )
  Speed# = 0.1
Else If( KeyDown(42) = 1 And KeyDown(29) = 0 )
  Speed# = 1
Else If( KeyDown(42) = 0 And KeyDown(29) = 1 )
  Speed# = 0.01
EndIf

If( KeyDown(17)=1 )
  MoveEntity(GhostRoot,0,0,Speed)
Else If( KeyDown(31)=1 )
  MoveEntity(GhostRoot,0,0,-Speed)
EndIf
If( KeyDown(30)=1 )
  MoveEntity(GhostRoot,-Speed,0,0)
Else If( KeyDown(32)=1 )
  MoveEntity(GhostRoot,Speed,0,0)
EndIf
If( KeyDown(16)=1 )
  MoveEntity(GhostRoot,0,-Speed,0)
Else If( KeyDown(18)=1 )
  MoveEntity(GhostRoot,0,Speed,0)
EndIf

End Function



STEVIE G

Quote from: RemiD on December 01, 2018, 17:43:39
done ! 8)

so in this new version, there are no notvisible tris (within the mesh), and the shapes are uvmapped and the texel size is almost uniform (but it also depends on how the chars of the font are structured, because fitmesh() can stretch some chars if they don't fill all extremities of the average char area (example a 12x16y area for a 16font))

;2dchars (pixels) to 3dchars (meshes) by RemiD (20181201)
;this will convert the 2d chars of a font to 3d chars meshes (made of uvmaped quads)
;this can work with any font, however if you want to keep a uniform texel size,
;it is preferable to use a font in which the chars have approximately the same width,height (for example a 12x16y area for a 16font),
;and each char fills most extremities of the 12x16y area (because of the use of fitmesh() which uses the vertices at the extremities to resize the mesh)

;graphics window
Graphics3D(640,480,32,2)

HidePointer()

SeedRnd(MilliSecs())

;Camera
Global Camera = CreateCamera()
CameraViewport(Camera,0,0,GraphicsWidth(),GraphicsHeight())
CameraRange(Camera,0.15,150)
CameraClsColor(Camera,000,000,000)

;Listener
Global Listener = CreateListener(Camera)

;Input
Global MX%, MY%
Global MXDiff%, MYDiff%

;Origine
Global Origine = CreateCube()
ScaleMesh(Origine,0.01,0.01,0.01)
EntityColor(Origine,255,000,255)
EntityFX(Origine,1)

;InteractionMode
Global InteractionMode%
Const C2D% = 1
Const C3D% = 2
InteractionMode = C3D
HidePointer()

;Ghost
Global GhostRoot
Global GhostRootYaw#
Global GhostEyes
Global GhostEyesPitch#
AddGhost()

;Chars
Global CharsCount%
Dim CharStr$(100)
Dim CharPWidth%(100)
Dim CharPHeight%(100)
Dim CharImage(100)
Dim CharMesh(100)
Dim CharTexture(100)

XFont = LoadFont("Blitz",16,False,False,False)
SetFont(XFont)

AllStr$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ;"abcdefghijklmnopqrstuvwxyz" ;"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
AllWidth% = StringWidth(AllStr) : AllHeight% = StringHeight(AllStr)

For n% = 1 To Len(AllStr) Step 1
CharsCount = CharsCount + 1 : I% = CharsCount
CharStr(I) = Mid(AllStr,n,1)
CharPWidth(I) = StringWidth(CharStr(I)) : CharPHeight(I) = StringHeight(CharStr(I))
DebugLog(CharStr(I)+" "+CharPWidth(I)+","+CharPHeight(I))
CharImage(I) = CreateImage(CharPWidth(I),CharPHeight(I))
SetBuffer(ImageBuffer(CharImage(I)))
ClsColor(Rand(025,255),Rand(025,255),Rand(025,255)) : Cls()
Color(125,125,125) : Text(0,0,CharStr(I))
CharMesh(I) = CreateMesh() : CreateSurface(CharMesh(I))
CharTexture(I) = CreateTexture(2048,8)
SetBuffer(TextureBuffer(CharTexture(I)))
  ClsColor(255,255,255) : Cls()
  For nn% = 1 To 256 Step 1
   SPX% = (nn-1)*8 : SPY% = 0
   ColR% = Rand(025,225) : ColG% = Rand(025,225) : ColB% = Rand(025,225)
   For PX% = SPX To SPX+8 Step 1
    For PY% = SPY To SPY+8 Step 1
     PR% = ColR+Rand(-012,+012) : PG% = ColG+Rand(-012,+012) : PB% = ColB+Rand(-012,+012)
     Color(PR,PG,PB) : Plot(PX,PY)
    Next
   Next
  Next
EntityTexture(CharMesh(I),CharTexture(I))
Next
DebugLog(CharsCount)

PX% = 0 : PY% = 0
SetBuffer(BackBuffer())
ClsColor(000,000,000) : Cls()
For I% = 1 To CharsCount Step 1
DrawImage(CharImage(I),PX,PY)
PX% = PX+ImageWidth(CharImage(I))
Next
Flip()
;WaitKey()

;pixels list
Global PixsCount%
Dim PixPX%(16*16)
Dim PixPY%(16*16)

;premodel + preuvmap the part which will compose the chars meshes (1texel corresponds to 0.125unit)
Global XPart = CreateMesh()
Surface = CreateSurface(XPart)
AddVertex(Surface,-0.5,+0.5,0.0) : VertexTexCoords(Surface,0,Float(0)/8,Float(0)/8)
AddVertex(Surface,+0.5,+0.5,0.0) : VertexTexCoords(Surface,1,Float(8)/8,Float(0)/8)
AddVertex(Surface,-0.5,-0.5,0.0) : VertexTexCoords(Surface,2,Float(0)/8,Float(8)/8)
AddVertex(Surface,+0.5,-0.5,0.0) : VertexTexCoords(Surface,3,Float(8)/8,Float(8)/8)
AddTriangle(Surface,0,1,2)
AddTriangle(Surface,2,1,3)
UpdateNormals(XPart)
HideEntity(XPart)

OffsetX# = 0 : OffsetY# = 0
;for each char
For I% = 1 To CharsCount Step 1

PixsCount = 0
PartsCount% = 0
VerticesCount% = 0
CPX% = 0 : CPY% = 0

SetBuffer(ImageBuffer(CharImage(I)))
;for each pixel in the char image
;list the pixels composing the char
For PX% = 0 To ImageWidth(CharImage(I))-1 Step 1
  For PY% = 0 To ImageHeight(CharImage(I))-1 Step 1
   GetColor(PX,PY)
   PixRed = ColorRed() : PixGreen = ColorGreen() : PixBlue = ColorBlue()
   If( PixRed = 125 And PixGreen = 125 And PixBlue = 125 )
    PixsCount = PixsCount + 1 : II% = PixsCount
    PixPX(II) = PX : PixPY(II) = PY
   EndIf
  Next
Next
;DebugLog(PixsCount)

Surface = GetSurface(CharMesh(I),1)
;for each pixel composing the char
For II% = 1 To PixsCount Step 1
  PX% = PixPX(II) : PY% = PixPY(II)

  ;add a part at front
  PartsCount = PartsCount + 1
  TPart = CopyMesh(XPart)
  X# = PX+0.5 : Y# = ImageHeight(CharImage(I))-PY+0.5 : Z# = -0.5
  PositionMesh(TPart,X,Y,Z)
  AddMesh(TPart,CharMesh(I)) : FreeEntity(TPart)
  VerticesCount = VerticesCount + 4
  CalculatePartUVs(Surface,VerticesCount-4,VerticesCount-3,VerticesCount-2,VerticesCount-1,CPX,CPY)
  CPX = CPX + 8 : CPY = 0

  ;add a part at back
  PartsCount = PartsCount + 1
  TPart = CopyMesh(XPart)
  RotateMesh(TPart,0,180,0)
  X# = PX+0.5 : Y# = ImageHeight(CharImage(I))-PY+0.5 : Z# = +0.5
  PositionMesh(TPart,X,Y,Z)
  AddMesh(TPart,CharMesh(I)) : FreeEntity(TPart)
  VerticesCount = VerticesCount + 4
  CalculatePartUVs(Surface,VerticesCount-4,VerticesCount-3,VerticesCount-2,VerticesCount-1,CPX,CPY)
  CPX = CPX + 8 : CPY = 0

  ;check if there is a neighboor at top
  ;if no
  If( IsPix(CharImage(I),PX,PY-1,125,125,125)=False )
   ;add a part on sidetop
   PartsCount = PartsCount + 1
   TPart = CopyMesh(XPart)
   RotateMesh(TPart,+90,0,0)
   X# = PX+0.5 : Y# = ImageHeight(CharImage(I))-PY+1.0 : Z# = 0
   PositionMesh(TPart,X,Y,Z)
   AddMesh(TPart,CharMesh(I)) : FreeEntity(TPart)
   VerticesCount = VerticesCount + 4
   CalculatePartUVs(Surface,VerticesCount-4,VerticesCount-3,VerticesCount-2,VerticesCount-1,CPX,CPY)
   CPX = CPX + 8 : CPY = 0
  EndIf

  ;check if there is a neighboor at bottom
  ;if no
  If( IsPix(CharImage(I),PX,PY+1,125,125,125)=False )
   ;add a part on sidebottom
   PartsCount = PartsCount + 1
   TPart = CopyMesh(XPart)
   RotateMesh(TPart,-90,0,0)
   X# = PX+0.5 : Y# = ImageHeight(CharImage(I))-PY+0 : Z# = 0
   PositionMesh(TPart,X,Y,Z)
   AddMesh(TPart,CharMesh(I)) : FreeEntity(TPart)
   VerticesCount = VerticesCount + 4
   CalculatePartUVs(Surface,VerticesCount-4,VerticesCount-3,VerticesCount-2,VerticesCount-1,CPX,CPY)
   CPX = CPX + 8 : CPY = 0
  EndIf

  ;check if there is a neighboor at left
  ;if no
  If( IsPix(CharImage(I),PX-1,PY,125,125,125)=False )
   ;add a part on sideleft
   PartsCount = PartsCount + 1
   TPart = CopyMesh(XPart)
   RotateMesh(TPart,0,-90,0)
   X# = PX+0 : Y# = ImageHeight(CharImage(I))-PY+0.5 : Z# = 0
   PositionMesh(TPart,X,Y,Z)
   AddMesh(TPart,CharMesh(I)) : FreeEntity(TPart)
   VerticesCount = VerticesCount + 4
   CalculatePartUVs(Surface,VerticesCount-4,VerticesCount-3,VerticesCount-2,VerticesCount-1,CPX,CPY)
   CPX = CPX + 8 : CPY = 0
  EndIf
 
  ;check if there is a neighboor at right
  ;if no
  If( IsPix(CharImage(I),PX+1,PY,125,125,125)=False )
   ;add a part on sideright
   PartsCount = PartsCount + 1
   TPart = CopyMesh(XPart)
   RotateMesh(TPart,0,+90,0)
   X# = PX+1 : Y# = ImageHeight(CharImage(I))-PY+0.5 : Z# = 0
   PositionMesh(TPart,X,Y,Z)
   AddMesh(TPart,CharMesh(I)) : FreeEntity(TPart)
   VerticesCount = VerticesCount + 4
   CalculatePartUVs(Surface,VerticesCount-4,VerticesCount-3,VerticesCount-2,VerticesCount-1,CPX,CPY)
   CPX = CPX + 8 : CPY = 0
  EndIf

Next
DebugLog(PartsCount)
DebugLog(VerticesCount)
DebugLog(CountVertices(GetSurface(CharMesh(I),1))+" "+CountTriangles(GetSurface(CharMesh(I),1)))

;resize the mesh to fit in an ideal area (for a 16font this would be a 12x16y area (in 2D) and a 1.2w1.6h0.2d area (in 3D))
FitMesh(CharMesh(I),0,0,0,1.2,1.6,0.2)

PositionEntity(CharMesh(I),OffsetX,OffsetY,0,True)
RotateEntity(CharMesh(I),Rand(-12.5,+12.5),0,0,True)

;OffsetX = OffsetX + CharPWidth(I)
;OffsetY = Rand(-2,+2)

OffsetX = OffsetX + (0.1+1.2+0.1)
OffsetY = Rnd(-0.2,+0.2)

;export the char image

;export the char mesh

Next

;DebugBox = CreateCube()
;ScaleMesh(DebugBox,Float(AllWidth)/2,Float(AllHeight)/2,1.0/2)
;PositionMesh(DebugBox,Float(AllWidth)/2,Float(AllHeight)/2,0)
;EntityAlpha(DebugBox,0.1)
;EntityFX(DebugBox,1)

;for each char

;load the char image

;load the char mesh

;specify a word

;write the word using 2d letters (in a 2d world with images)

;write the word using 3d letters (in a 3d world with meshes)

;light
DLight = CreateLight(1)
LightColor(DLight,255,255,255)
PositionEntity(DLight,0,1000,-1000,True)
RotateEntity(DLight,45,0,0,True)

AmbientLight(025,025,025)

PositionEntity(GhostRoot,0,0+1.65,-3,True)
GhostRootYaw = 0 : GhostEyesPitch = 0

Global MainLoopTimer = CreateTimer(30)

Main()

End()

Function Main()

Repeat

  MainLoopMilliStart% = MilliSecs()

  MX = MouseX() : MY = MouseY()

  MXDiff = MouseXSpeed() : MYDiff = MouseYSpeed()

  UpdateInteractionMode()
  If( InteractionMode = C2D )
   ;
  Else If( InteractionMode = C3D )
   UpdateGhost()    
  EndIf    

  WireFrame(False)
  If( KeyDown(2)=1 )
   WireFrame(True)
  EndIf

  PositionEntity(Camera,EntityX(GhostEyes,True),EntityY(GhostEyes,True),EntityZ(GhostEyes,True),True)
  RotateEntity(Camera,EntityPitch(GhostEyes,True),EntityYaw(GhostEyes,True),EntityRoll(GhostEyes,True),True)

  CameraViewport(Camera,0,0,GraphicsWidth(),GraphicsHeight())
  CameraClsColor(Camera,000,000,000)
  SetBuffer(BackBuffer())
  RenderWorld()

  Color(255,255,255)
  If( KeyDown(3)=1 )
   Text(0,0,"Tris = "+TrisRendered())
   Text(0,15,"FPS = "+FPS)
  EndIf

  ;Flip(1)
  WaitTimer(MainLoopTimer)
  VWait():Flip(False)

  MainLoopMilliTime = MilliSecs() - MainLoopMilliStart
  If( MainLoopMilliTime < 1 )
   MainLoopMilliTime = 1
  EndIf

  FPS% = 1000.0/MainLoopMilliTime

Until( KeyDown(1)=1 )

End Function

Function IsPix%(Image,PX%,PY%,PR%,PG%,PB%)

SetBuffer(ImageBuffer(Image))
GetColor(PX,PY)
PixRed = ColorRed() : PixGreen = ColorGreen() : PixBlue = ColorBlue()
If( PixRed = PR And PixGreen = PG And PixBlue = PB )
  Return True
Else
  Return False
EndIf

End Function

Function CalculatePartUVs(Surface,V0I%,V1I%,V2I%,V3I%,CPX%,CPY%)

V0U# = Float(CPX+0)/2048 : V0V# = Float(CPY+0)/8 : VertexTexCoords(Surface,V0I,V0U,V0V)
V1U# = Float(CPX+8)/2048 : V1V# = Float(CPY+0)/8 : VertexTexCoords(Surface,V1I,V1U,V1V)
V2U# = Float(CPX+0)/2048 : V2V# = Float(CPY+8)/8 : VertexTexCoords(Surface,V2I,V2U,V2V)
V3U# = Float(CPX+8)/2048 : V3V# = Float(CPY+8)/8 : VertexTexCoords(Surface,V3I,V3U,V3V)

End Function

Function UpdateInteractionMode()

If( KeyHit(15)=1 )
  If( InteractionMode = C2D )
   InteractionMode = C3D
   HidePointer()
  Else If( InteractionMode = C3D )
   InteractionMode = C2D
   ShowPointer()
  EndIf
EndIf

End Function

Function AddGhost()

GhostRoot = CreatePivot()

GhostEyes = CreatePivot()

EntityParent(GhostEyes,GhostRoot,True)

End Function

Function UpdateGhost()

MoveMouse(GraphicsWidth()/2,GraphicsHeight()/2)
GhostEyesPitch = GhostEyesPitch + Float(MYDiff)/10
If( GhostEyesPitch < -89 )
  GhostEyesPitch = -89
Else If( GhostEyesPitch > 89 )
  GhostEyesPitch = 89
EndIf
RotateEntity(GhostEyes,GhostEyesPitch,0,0,False)
GhostRootYaw = GhostRootYaw - Float(MXDiff)/10
RotateEntity(GhostRoot,0,GhostRootYaw,0,False)

If( KeyDown(42) = 0 And KeyDown(29) = 0 )
  Speed# = 0.1
Else If( KeyDown(42) = 1 And KeyDown(29) = 0 )
  Speed# = 1
Else If( KeyDown(42) = 0 And KeyDown(29) = 1 )
  Speed# = 0.01
EndIf

If( KeyDown(17)=1 )
  MoveEntity(GhostRoot,0,0,Speed)
Else If( KeyDown(31)=1 )
  MoveEntity(GhostRoot,0,0,-Speed)
EndIf
If( KeyDown(30)=1 )
  MoveEntity(GhostRoot,-Speed,0,0)
Else If( KeyDown(32)=1 )
  MoveEntity(GhostRoot,Speed,0,0)
EndIf
If( KeyDown(16)=1 )
  MoveEntity(GhostRoot,0,-Speed,0)
Else If( KeyDown(18)=1 )
  MoveEntity(GhostRoot,0,Speed,0)
EndIf

End Function


You can get around the extremities issues by adding dummy vertices at the extremities for each character mesh.  You don't have to add any triangles.  Had to use something like this when building my own 3d font for Polymaniacs.

RemiD

Quote
You can get around the extremities issues by adding dummy vertices at the extremities for each character mesh.
yes good idea, thanks  :)

but i think that it is preferable to slightly edit an existing font, or to make your own font, because some chars can cause problems (for example "I" "i") because depending on the font, the char is not centered in the 12x16y area, so even if you don't stretch it (by adding vertices at each corner, like you suggest) it still looks weird imo. It depends how the author has decided to draw some chars...

RemiD

after some tests, i decided to model / uvmap each character (letter) manually, because i plan to draw on the texels of the texture (but directly on the shape), and if i want it to look good and without noticeable seams, the uvmapping must be done in a specific way, which would be difficult to achieve in code...
15 letters already made, 11 remaining  :-\