Main Menu

News:

SyntaxBoom, now with pwetty syntax highlighted code boxes! \o/ 

https://www.syntaxboom.com/forum/index.php?topic=96

Shoutbox

Jackdaw

Today at 22:07:30
Well you learn something new every day. There is a fork of Blender 3D called Bforartists, that has a much better GUI than Blender's own. You'll find the link to it in the new github page in found in https://www.syntaxboom.com/forum/index.php?topic=25.0

Baggey

2025-09-24, 17:57:11
They'll be using Expanding foam to glue bricks together next  :-X

Dabzy

2025-09-24, 06:09:52
You can also get the expanding foam post fix, but, I wouldnt trust it really, especially where I live on the side of a valley and when the storms blow in the right direction, whistling down the valley, nowt is safe!

Baggey

2025-09-23, 08:53:01
That Postcrete stuff is amazing. I never know how much water to add. May be i should read the Instructions  ;D 

Dabzy

2025-09-22, 21:33:46
Cannot beat a breaky uppy mode, saves the hand cramps and chipped knuckles knocking ten bells out of a chod of conc with a hammer and chisel.

GfK

2025-09-22, 21:28:44
I have a massive JCB drill with a concrete breaky uppy mode which has got me out of jail free a couple of times replacing rotted fence posts that has been concreted in.

Amon

2025-09-22, 19:23:30
What about Roly?

Dabzy

2025-09-22, 19:22:35
Putting my 2 deckings in.... I've dug enough post holes to last me a life time... I feel sorry for the future poor sod who may want to shift'em... Like most things I do, I tend to go over the top, and as such, I've probably got shares in postcrete! :D

GfK

2025-09-22, 19:10:57
Round is a shape.

Baggey

2025-09-22, 19:04:49
Consult a qualified electrician for compliance with BS 7671 and local building Regs! Avoid areas where future digging is likely. ;)

Members
  • Total Members: 55
  • Latest: Amon
Stats
  • Total Posts: 1,608
  • Total Topics: 198
  • Online today: 13
  • Online ever: 54 (Sep 14, 2025, 08:48 AM)
Users Online
  • Users: 0
  • Guests: 6
  • Total: 6
Welcome to SyntaxBoom. Please login or sign up.

Recent

.obj loader and exporter for b3d

Started by Matty, Jul 05, 2025, 05:20 PM

Previous topic - Next topic

Matty

Self explanatory. Not optimised however.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;This "Blitz3d Tool" can be used
;to load .obj models, and to convert
;them from models that use multiple
;texture files and materials into a
;single texture file and material.
;Eventually I'll use it in a batch
;process to convert all my 3drt.com
;models that have multiple texture
;materials into single texture objects
;so I can use it with raylib without
;changing my model loading code.

Type obj_vert
	Field id
	Field x#,y#,z#
End Type

Type obj_uv
	Field id
	Field u#,v#
End Type

Type obj_tri
	Field groupid
	Field grouptex
	Field vert_id0,uv_id0,vert_id1,uv_id1,vert_id2,uv_id2
End Type

;Graphics3D 800,600,0,2
;convertobj("mesh1892.obj","output1892.obj")
;End

Function convertobj(inputfile$,outputfile$)
	ent = loadobj(inputfile)
;	demomesh(ent,"dummy.bmp")
	If ent<>0 Then 
		ent2 = multi2singlesurface(ent)
		If ent2<>0 Then 
			saveobj(ent2,outputfile)
			FreeEntity ent2
		EndIf 
		FreeEntity ent
	EndIf 
End Function 

Function loadobj(file$)
Delete Each obj_vert
Delete Each obj_uv
Delete Each obj_tri
Delete Each obj_textures
;program for loading very simple obj files
;these obj files must have only 1 surface and 1 group
;it also doesn't load material files.
;so it's useful for very simple object meshes
;more complexity can be added later
infile = ReadFile(file$)
If infile<>0 Then 
	vid = 0
	uvid = 0
	triid = 0
	groupid = 1
	grouptex = 0
	While(Not(Eof(infile)))
		val$ = ReadLine(infile)
		If Left(val,1) = "g" Then
			groupid = groupid + 1
			grouptex = 0
		EndIf 
		If Left(val,6) = "usemtl" Then
			groupid = groupid + 1
			grouptex = 0	
			mtl$ = Mid(val,8,Len(val) - 7)
			infile2 = ReadFile(Left(file,Len(file)-3)+"mtl")
			If infile2<>0 Then
				While(Not(Eof(infile2)))
					val2$ = ReadLine(infile2)
					If Left(val2,6) = "newmtl" Then
						If Mid(val2,8,Len(val2)-7) = mtl Then 
							remains = True
							While remains
								If Eof(infile2) Then Exit
								val3$ = ReadLine(infile2)
								If val3 = "" Then Exit 
								If Left(val3,6) = "map_Kd" Then 
									texfile$ = Mid(val3,8,Len(val3)-7)
									texfile = texcorrectpath(file,texfile)
									If Right(Lower(texfile),3) = "jpg" Or Right(Lower(texfile),3) = "bmp" Then
										grouptex = LoadTexture(texfile,1)
										If grouptex<>0 Then recordtexfile(texfile)
									Else
										;grouptex = LoadTexture(texfile,1+2)
										grouptex = LoadTexture(texfile,1) ;png sometimes ends up being transparent when it shouldn't be...partial bug fix
										If grouptex<>0 Then 
											recordtexfile(texfile)
											anyalpha = 0
											SetBuffer TextureBuffer(grouptex)
											For ix = 0 To TextureWidth(grouptex) - 1
												For iy = 0 To TextureHeight(grouptex) - 1
													rgba = (ReadPixel(ix,iy,TextureBuffer(grouptex)) Shr 24) And 255
													If rgba < 255 Then 
														anyalpha = 1
														Exit
													EndIf 
												Next
												If anyalpha<>0 Then Exit
											Next
											If anyalpha = 0 Then 
												FreeTexture grouptex
												grouptex = LoadTexture(texfile,1)
											EndIf 
											SetBuffer BackBuffer()
										EndIf 
									EndIf 
									
								EndIf 
							Wend 	
						EndIf 
					EndIf 
				Wend 
				CloseFile infile2
			EndIf 
		EndIf
		If Left(val,1) = "f" Then 
			txt$ = ""
			tri = 0
			
			objt.obj_tri = New obj_tri
			objt\groupid = groupid
			objt\grouptex = grouptex
			triid = 0
			For i = 3 To Len(val)
				txt = txt + Mid(val,i,1)
				If Mid(val,i,1) = " " Or i = Len(val) Then
					triid = triid + 1
					txt2$ = ""
					tri = tri + 1
					el = 0
					For j = 1 To Len(txt)
						txt2 = txt2 + Mid(txt,j,1)
						If Mid(txt,j,1) = "/" Then
							idtxt$ = Left(txt2,Len(txt2)-1)
							el = el + 1
							If el = 1 Then 
								;vert
								If triid = 1 Then
									objt\vert_id0 = Int(idtxt)
								EndIf
								If triid = 2 Then
									objt\vert_id1 = Int(idtxt)
								EndIf
								If triid = 3 Then
									objt\vert_id2 = Int(idtxt)
								EndIf 
							
							EndIf
							If el = 2
								;uv	
								If triid = 1 Then
									objt\uv_id0 = Int(idtxt)
								EndIf
								If triid = 2 Then
									objt\uv_id1 = Int(idtxt)
								EndIf
								If triid = 3 Then
									objt\uv_id2 = Int(idtxt)
								EndIf 
							
							EndIf 
							txt2 = ""
						EndIf
					Next
					txt = ""
				EndIf 
			Next
		EndIf 
		If Left(val,2) = "vt" Then
			uvid = uvid + 1
			objuv.obj_uv = New obj_uv
			txt$ = ""
			index = 0
			For i = 4 To Len(val)
				txt = txt + Mid(val,i,1)
				If Mid(val,i,1) = " " Then
					;u
					objuv\id = uvid
					txt = Left(txt,Len(txt)-1)
					objuv\u = Float(txt)
					txt = ""
				EndIf 
				If i = Len(val)
					objuv\v = Float(txt)
				EndIf 
			Next
		EndIf 
		If Left(val,2) = "v " Then
			vid = vid + 1
			objv.obj_vert = New obj_vert
			txt$ = ""
			index = 0
			For i = 3 To Len(val)
				txt = txt + Mid(val,i,1)
				If i = Len(val)
						vv# = Float(txt)
						objv\z = vv
				EndIf 
				If Mid(val,i,1) = " " Then
					txt = Left(txt,Len(txt)-1)
					vv# = Float(txt)
					txt = ""
					index = index + 1
					If index = 1 Then 
						objv\id = vid
						objv\x = vv
					EndIf
					If index = 2 Then
						objv\y = vv
					EndIf 
				EndIf 
			Next
		EndIf 
	Wend 
	CloseFile infile
Else
	Return 0
EndIf 

mesh = CreateMesh()
;surf = CreateSurface(mesh)
groupid = 0
grouptex = 0
ogroupid = -1
For objt.obj_tri = Each obj_tri
	groupid = objt\groupid
	grouptex = objt\grouptex
	If groupid <> ogroupid And groupid > 0 Then 
		ogroupid = groupid
		surf = CreateSurface(mesh)

		If grouptex<>0 Then 
			brush = CreateBrush()
			BrushTexture brush,grouptex
			PaintSurface surf,brush
		EndIf 
	EndIf 
	v0.obj_vert = Null
	v1.obj_vert = Null
	v2.obj_vert = Null
	uv0.obj_uv = Null
	uv1.obj_uv = Null
	uv2.obj_uv = Null
	For objv.obj_vert = Each obj_vert
		If objv\id = objt\vert_id0 Then
			v0 = objv
		EndIf
		If objv\id = objt\vert_id1 Then
			v1 = objv
		EndIf
		If objv\id = objt\vert_id2 Then
			v2 = objv
		EndIf
	Next

	For objuv.obj_uv = Each obj_uv
		If objuv\id = objt\uv_id0 Then
			uv0 = objuv
		EndIf 
		If objuv\id = objt\uv_id1 Then
			uv1 = objuv
		EndIf 
		If objuv\id = objt\uv_id2 Then
			uv2 = objuv
		EndIf 
	Next
		sv0 = AddVertex(surf,v0\x,v0\y,v0\z,uv0\u,1.0-uv0\v)
		sv1 = AddVertex(surf,v1\x,v1\y,v1\z,uv1\u,1.0-uv1\v)
		sv2 = AddVertex(surf,v2\x,v2\y,v2\z,uv2\u,1.0-uv2\v)
		AddTriangle surf,sv0,sv1,sv2
		c = c + 1
Next
UpdateNormals mesh
Return mesh
End Function 

Function saveobj(mesh,file$)
;creates a single surface mesh from an existing mesh
;and outputs it as an .obj file
;very non optimised version of the file though
;but it will come in handy when I get around to making a game
;with the other 3drt meshes that AREN'T texture the way I need
;for my games. (I need a single texture and a single surface for a 
;single shader to apply to a single material per model in raylib
;the way I do things...this is a start on that process)
If mesh = 0 Then RuntimeError("Mesh does not exist")
outfile = WriteFile(file)
If outfile = 0 Then RuntimeError("Could not save to file:"+file)
		tmp = CreateMesh()
		surf2 = CreateSurface(tmp)
		For s = 1 To CountSurfaces(mesh)
			surf = GetSurface(mesh,s)
			For tri = 0 To CountTriangles(surf) - 1
				v0 = TriangleVertex(surf,tri,0)
				v1 = TriangleVertex(surf,tri,1)
				v2 = TriangleVertex(surf,tri,2)
				;nx# = (VertexNX(surf,v0) + VertexNX(surf,v1) + VertexNX(surf,v2))*0.333
				;ny# = (VertexNY(surf,v0) + VertexNY(surf,v1) + VertexNY(surf,v2))*0.333
				;nz# = (VertexNZ(surf,v0) + VertexNZ(surf,v1) + VertexNZ(surf,v2))*0.333
				;nn# = Sqr(nx*nx+ny*ny+nz*nz)
				;If nn <> 0 Then
				;	nx = nx / nn
				;	ny = ny / nn
				;	nz = nz / nn
				;EndIf 
				;dx# = 0
				;dy# = 1
				;dz# = 0
				;dot# = dx*nx+dy*ny+dz*nz
				;dx# = -1
				;dy# = 0
				;dz# = 0
				;dot2# = dx*nx+dy*ny+dz*nz

				;If dot > -0.65 And dot2 > -0.65 Then 
					v00 = AddVertex(surf2,VertexX(surf,v0),VertexY(surf,v0),VertexZ(surf,v0),VertexU(surf,v0),VertexV(surf,v0))
					v11 = AddVertex(surf2,VertexX(surf,v1),VertexY(surf,v1),VertexZ(surf,v1),VertexU(surf,v1),VertexV(surf,v1))
					v22 = AddVertex(surf2,VertexX(surf,v2),VertexY(surf,v2),VertexZ(surf,v2),VertexU(surf,v2),VertexV(surf,v2))
					AddTriangle surf2,v00,v11,v22
				;EndIf 
			Next
		Next
		UpdateNormals tmp
		For v = 0 To CountVertices(surf2) - 1
			vx# = VertexX(surf2,v)
			vy# = VertexY(surf2,v)
			vz# = VertexZ(surf2,v)
			txt0$ = Str(vx)
			txt1$ = Str(vy)
			txt2$ = Str(vz)
			If Abs(vx)<0.001 Then txt0 = "0.0"
			If Abs(vy)<0.001 Then txt1 = "0.0"
			If Abs(vz)<0.001 Then txt2 = "0.0"
			WriteLine outfile,"v "+txt0+" "+txt1+" "+txt2
		Next
		For vt = 0 To CountVertices(surf2) - 1
			uu# = VertexU(surf2,vt)
			vv# = VertexV(surf2,vt)
			txt1 = Str(uu)
			txt2 = Str(1.0-vv)
			If Abs(uu)<0.001 Then txt1 = "0.0"
			If Abs(vv)<0.001 Then txt2 = "0.0"
			WriteLine outfile,"vt "+txt1+" "+txt2
		Next
		For vn = 0 To CountVertices(surf2) - 1
			xn# = VertexNX(surf2,vn)
			yn# = VertexNY(surf2,vn)
			zn# = VertexNZ(surf2,vn)
			txt0 = Str(xn)
			txt1 = Str(yn)
			txt2 = Str(zn)
			If Abs(xn)<0.001 Then txt0 = "0.0"
			If Abs(yn)<0.001 Then txt1 = "0.0"
			If Abs(zn)<0.001 Then txt2 = "0.0"
			WriteLine outfile,"vn "+txt0+" "+txt1+" "+txt2
		Next
		For tri = 0 To CountTriangles(surf2)-1
			v0 = TriangleVertex(surf2,tri,0) + 1
			v1 = TriangleVertex(surf2,tri,1) + 1
			v2 = TriangleVertex(surf2,tri,2) + 1
			WriteLine outfile,"f "+Str(v0)+"/"+Str(v0)+"/"+Str(v0)+" "+Str(v1)+"/"+Str(v1)+"/"+Str(v1)+" "+Str(v2)+"/"+Str(v2)+"/"+Str(v2)
		Next
		CloseFile outfile
		FreeEntity tmp
End Function 

Function texcorrectpath$(file$,texfile$)
If Instr(texfile,".\")>0 Then
	texfile = Replace(texfile,".\","")
EndIf
If Instr(texfile,"\\")>0 Then 
	texfile = Replace(texfile,"\\","\")
EndIf 
If FileType(texfile) = 1 Then Return texfile
If Instr(texfile,"\")>0 Or Instr(texfile,"/")>0 Then 
	;if this is the case it means the texfile value is something
	;weird like ".\" at the start of it....probably...
	;otherwise it should have found it....
	;if that's the case...then strip out the values up to and including 
	;the slash and then do what we do below...
	;otherwise yeah...weird file format.....
	;at least....it's not how I've usually arranged my obj files
	txt$ = ""
	For i = 1 To Len(texfile)
		If Mid(texfile,i,1)="\" Or Mid(texfile,i,1)="/" Then
			texfile = Mid(texfile,i+1,Len(texfile) - i)
			If Instr(texfile,"\")=0 And Instr(texfile,"/")=0 Then Return texfile
		EndIf 
	Next
Else
	If Instr(file,"\")>0 Or Instr(file,"/")>0 Then 
		For i = Len(file) To 1 Step - 1
			If Mid(file,i,1)="\" Or Mid(file,i,1)="/" Then
				texfile = Left(file,i) + texfile
				Return texfile
			EndIf 
		Next
	Else
		;weird that we can't find it in this case....
		;error....
	EndIf 
EndIf 
Return texfile$
End Function 

;;;;;this function is for blitz3d for my games at home to enable me to take multi surface, multi textured .obj models
;and convert them into a single surface, single texture, .obj model that I can easily load into raylib programs without
;significant changes and without having to worry about so many performance issues.

;the import routines and the export routines already exist..the only part I need to write here and upload to my website
;is the process of reducing the quantity of textures to a single texture that contains the other textures and generate
;a mesh with the right uvs for it.

;it means making a type object to store the texture names that will have to be added possibly to the import routine.
;that's easy enough to do, when you load the texture, store it in an object with both the short name and the full path.
;I'll include something similar here but it will need to modify some stuff at the PC at home when I include this...assuming
;it works as expected

Const MULTI_DEBUG = 1;

Type obj_textures
	Field shortname$
	Field fullname$
	Field index; value....numeric count of which texture this is in the list from 0 - n*m roughly
	Field imagehandle ;you'll see why we use an image not a texture later
	;don't include the handle we'll get that elsewhere
	;assume this is populated once we get to the function, by the import loader function
	;don't have to build it here
End Type

Function recordtexfile(texfile$)
	index = 0
	For ot.obj_textures = Each obj_textures
		If ot\fullname = texfile Then 
			Return
		EndIf 
		index = index + 1
	Next	
	ot.obj_textures = New obj_textures
	ot\fullname = texfile
	txt$ = texfile
	For i = Len(txt) To 1 Step - 1
		If Mid(txt,i,1) = "\" Or Mid(txt,i,1)="/" Then
			txt = Mid(txt,i+1,Len(txt)-i)
			ot\shortname = txt
			ot\index = index
			Return
		EndIf 
	Next
	ot\shortname = txt
	ot\index = index
End Function 

Function multi2singlesurface(ent)
	If ent = 0 Then Return 
	counttex = 0
	For ot.obj_textures = Each obj_textures
		counttex = counttex + 1
	Next

	;assume each texture is 512x512...we'll build it as if they are..whether or not they actually are
	;we'll be converting the textures to 512x512 anyway....
	n = Sqr(counttex)
	m = n + 1
	finaltex = CreateImage(512*n,512*m)
	mesh = CreateMesh()
	surf2 = CreateSurface(mesh)
	uu# = 0;
	vv# = 0;
	uww# = 1.0 / Float(n)
	vhh# = 1.0 / Float(m)
	ni = 0
	mi = 0
	For s = 1 To CountSurfaces(ent)
		surf = GetSurface(ent,s)
		brush = GetSurfaceBrush(surf)
		If brush<>0 Then 
			tex0 = GetBrushTexture(brush) ;not sure if syntax correct here
			If tex0<>0 Then 
				tex0name$ = TextureName(tex0)
				;look up the texture from the obj_textures list..in the shortname
				;it MUST exist - otherwise how did it get into the file?
				For ot.obj_textures = Each obj_textures
					If Instr(Lower(ot\fullname),Lower(tex0name))>0 Or Instr(Lower(tex0name),Lower(ot\fullname))>0Then 
						;yep, we've found the texture
						img = LoadImage(ot\fullname)
						ResizeImage img,512,512 ;ignore whatever size it really is
						ni = ot\index Mod n
						mi = (ot\index - ni) / n
						ux = 512 * ni
						vy = 512 * mi
						CopyRect 0,0,512,512,ux,vy,ImageBuffer(img),ImageBuffer(finaltex)
						FreeImage img
						;now go through all the triangles in this surface...and rebuild the mesh
						;with the same x/y/z coordinates but with the uvs altered as follows
						;divide them by uww and vhh, then add ni * uww and mi * vhh to their position
						For tri = 0 To CountTriangles(surf) - 1
							v0 = TriangleVertex(surf,tri,0)
							v1 = TriangleVertex(surf,tri,1)
							v2 = TriangleVertex(surf,tri,2)
							v0x# = VertexX(surf,v0)
							v0y# = VertexY(surf,v0)
							v0z# = VertexZ(surf,v0)
							v0u# = VertexU(surf,v0)
							v0v# = VertexV(surf,v0)
							v0u# = v0u# * uww + Float(ni) * uww
							v0v# = v0v# * vhh + Float(mi) * vhh
							
							
							v1x# = VertexX(surf,v1)
							v1y# = VertexY(surf,v1)
							v1z# = VertexZ(surf,v1)
							v1u# = VertexU(surf,v1)
							v1v# = VertexV(surf,v1)
							v1u# = v1u# * uww + Float(ni) * uww
							v1v# = v1v# * vhh + Float(mi) * vhh
							
							v2x# = VertexX(surf,v2)
							v2y# = VertexY(surf,v2)
							v2z# = VertexZ(surf,v2)
							v2u# = VertexU(surf,v2)
							v2v# = VertexV(surf,v2)
							v2u# = v2u# * uww + Float(ni) * uww
							v2v# = v2v# * vhh + Float(mi) * vhh
							
							;now create a bunch of new vertices and add them and their triangles to the new surf, surf2
							newv0 = AddVertex( surf2,v0x,v0y,v0z,v0u,v0v#)
							newv1 = AddVertex( surf2,v1x,v1y,v1z,v1u,v1v#)
							newv2 = AddVertex( surf2,v2x,v2y,v2z,v2u,v2v#)
							
							AddTriangle surf2,newv0,newv1,newv2
							
						Next
					EndIf 
				Next
				FreeTexture tex0
			EndIf 
			FreeBrush brush
		EndIf 
	Next
	
	SaveImage finaltex,"output_tex.bmp"
	FreeImage finaltex
	UpdateNormals mesh
	If MULTI_DEBUG<>0 Then 
		;apply the output_tex as a texture to the new mesh...and display it rotating around....
		demomesh(mesh,"output_tex.bmp")
	EndIf 
	Return mesh
End Function 

Function demomesh(ent,texfile$)
	tmpcam = CreateCamera()
	PositionEntity ent,0,0,MeshDepth(ent)*3.0
	tex = LoadTexture(texfile,1)
	If tex<>0 Then EntityTexture ent,tex
	EntityFX ent,1
	FlushKeys
	Repeat
		Cls
		TurnEntity ent,0.1,0.2,0.3
		RenderWorld
		Text 0,0,"Press Escape to Proceed, but verify the mesh looks as you expect it to look"
		Text 0,30,"Press 'x' to exit the program totally."
		Flip True
		Delay 10
		If KeyHit(45)<>0 Then End
	Until KeyHit(1)
	FreeEntity tmpcam
	FreeTexture tex
End Function

Baggey

Quote from: Matty on Jul 05, 2025, 05:20 PMSelf explanatory. Not optimised however.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;This "Blitz3d Tool" can be used
;to load .obj models, and to convert
;them from models that use multiple
;texture files and materials into a
;single texture file and material.
;Eventually I'll use it in a batch
;process to convert all my 3drt.com
;models that have multiple texture
;materials into single texture objects
;so I can use it with raylib without
;changing my model loading code.

Type obj_vert
	Field id
	Field x#,y#,z#
End Type

Type obj_uv
	Field id
	Field u#,v#
End Type

Type obj_tri
	Field groupid
	Field grouptex
	Field vert_id0,uv_id0,vert_id1,uv_id1,vert_id2,uv_id2
End Type

;Graphics3D 800,600,0,2
;convertobj("mesh1892.obj","output1892.obj")
;End

Function convertobj(inputfile$,outputfile$)
	ent = loadobj(inputfile)
;	demomesh(ent,"dummy.bmp")
	If ent<>0 Then 
		ent2 = multi2singlesurface(ent)
		If ent2<>0 Then 
			saveobj(ent2,outputfile)
			FreeEntity ent2
		EndIf 
		FreeEntity ent
	EndIf 
End Function 

Function loadobj(file$)
Delete Each obj_vert
Delete Each obj_uv
Delete Each obj_tri
Delete Each obj_textures
;program for loading very simple obj files
;these obj files must have only 1 surface and 1 group
;it also doesn't load material files.
;so it's useful for very simple object meshes
;more complexity can be added later
infile = ReadFile(file$)
If infile<>0 Then 
	vid = 0
	uvid = 0
	triid = 0
	groupid = 1
	grouptex = 0
	While(Not(Eof(infile)))
		val$ = ReadLine(infile)
		If Left(val,1) = "g" Then
			groupid = groupid + 1
			grouptex = 0
		EndIf 
		If Left(val,6) = "usemtl" Then
			groupid = groupid + 1
			grouptex = 0	
			mtl$ = Mid(val,8,Len(val) - 7)
			infile2 = ReadFile(Left(file,Len(file)-3)+"mtl")
			If infile2<>0 Then
				While(Not(Eof(infile2)))
					val2$ = ReadLine(infile2)
					If Left(val2,6) = "newmtl" Then
						If Mid(val2,8,Len(val2)-7) = mtl Then 
							remains = True
							While remains
								If Eof(infile2) Then Exit
								val3$ = ReadLine(infile2)
								If val3 = "" Then Exit 
								If Left(val3,6) = "map_Kd" Then 
									texfile$ = Mid(val3,8,Len(val3)-7)
									texfile = texcorrectpath(file,texfile)
									If Right(Lower(texfile),3) = "jpg" Or Right(Lower(texfile),3) = "bmp" Then
										grouptex = LoadTexture(texfile,1)
										If grouptex<>0 Then recordtexfile(texfile)
									Else
										;grouptex = LoadTexture(texfile,1+2)
										grouptex = LoadTexture(texfile,1) ;png sometimes ends up being transparent when it shouldn't be...partial bug fix
										If grouptex<>0 Then 
											recordtexfile(texfile)
											anyalpha = 0
											SetBuffer TextureBuffer(grouptex)
											For ix = 0 To TextureWidth(grouptex) - 1
												For iy = 0 To TextureHeight(grouptex) - 1
													rgba = (ReadPixel(ix,iy,TextureBuffer(grouptex)) Shr 24) And 255
													If rgba < 255 Then 
														anyalpha = 1
														Exit
													EndIf 
												Next
												If anyalpha<>0 Then Exit
											Next
											If anyalpha = 0 Then 
												FreeTexture grouptex
												grouptex = LoadTexture(texfile,1)
											EndIf 
											SetBuffer BackBuffer()
										EndIf 
									EndIf 
									
								EndIf 
							Wend 	
						EndIf 
					EndIf 
				Wend 
				CloseFile infile2
			EndIf 
		EndIf
		If Left(val,1) = "f" Then 
			txt$ = ""
			tri = 0
			
			objt.obj_tri = New obj_tri
			objt\groupid = groupid
			objt\grouptex = grouptex
			triid = 0
			For i = 3 To Len(val)
				txt = txt + Mid(val,i,1)
				If Mid(val,i,1) = " " Or i = Len(val) Then
					triid = triid + 1
					txt2$ = ""
					tri = tri + 1
					el = 0
					For j = 1 To Len(txt)
						txt2 = txt2 + Mid(txt,j,1)
						If Mid(txt,j,1) = "/" Then
							idtxt$ = Left(txt2,Len(txt2)-1)
							el = el + 1
							If el = 1 Then 
								;vert
								If triid = 1 Then
									objt\vert_id0 = Int(idtxt)
								EndIf
								If triid = 2 Then
									objt\vert_id1 = Int(idtxt)
								EndIf
								If triid = 3 Then
									objt\vert_id2 = Int(idtxt)
								EndIf 
							
							EndIf
							If el = 2
								;uv	
								If triid = 1 Then
									objt\uv_id0 = Int(idtxt)
								EndIf
								If triid = 2 Then
									objt\uv_id1 = Int(idtxt)
								EndIf
								If triid = 3 Then
									objt\uv_id2 = Int(idtxt)
								EndIf 
							
							EndIf 
							txt2 = ""
						EndIf
					Next
					txt = ""
				EndIf 
			Next
		EndIf 
		If Left(val,2) = "vt" Then
			uvid = uvid + 1
			objuv.obj_uv = New obj_uv
			txt$ = ""
			index = 0
			For i = 4 To Len(val)
				txt = txt + Mid(val,i,1)
				If Mid(val,i,1) = " " Then
					;u
					objuv\id = uvid
					txt = Left(txt,Len(txt)-1)
					objuv\u = Float(txt)
					txt = ""
				EndIf 
				If i = Len(val)
					objuv\v = Float(txt)
				EndIf 
			Next
		EndIf 
		If Left(val,2) = "v " Then
			vid = vid + 1
			objv.obj_vert = New obj_vert
			txt$ = ""
			index = 0
			For i = 3 To Len(val)
				txt = txt + Mid(val,i,1)
				If i = Len(val)
						vv# = Float(txt)
						objv\z = vv
				EndIf 
				If Mid(val,i,1) = " " Then
					txt = Left(txt,Len(txt)-1)
					vv# = Float(txt)
					txt = ""
					index = index + 1
					If index = 1 Then 
						objv\id = vid
						objv\x = vv
					EndIf
					If index = 2 Then
						objv\y = vv
					EndIf 
				EndIf 
			Next
		EndIf 
	Wend 
	CloseFile infile
Else
	Return 0
EndIf 

mesh = CreateMesh()
;surf = CreateSurface(mesh)
groupid = 0
grouptex = 0
ogroupid = -1
For objt.obj_tri = Each obj_tri
	groupid = objt\groupid
	grouptex = objt\grouptex
	If groupid <> ogroupid And groupid > 0 Then 
		ogroupid = groupid
		surf = CreateSurface(mesh)

		If grouptex<>0 Then 
			brush = CreateBrush()
			BrushTexture brush,grouptex
			PaintSurface surf,brush
		EndIf 
	EndIf 
	v0.obj_vert = Null
	v1.obj_vert = Null
	v2.obj_vert = Null
	uv0.obj_uv = Null
	uv1.obj_uv = Null
	uv2.obj_uv = Null
	For objv.obj_vert = Each obj_vert
		If objv\id = objt\vert_id0 Then
			v0 = objv
		EndIf
		If objv\id = objt\vert_id1 Then
			v1 = objv
		EndIf
		If objv\id = objt\vert_id2 Then
			v2 = objv
		EndIf
	Next

	For objuv.obj_uv = Each obj_uv
		If objuv\id = objt\uv_id0 Then
			uv0 = objuv
		EndIf 
		If objuv\id = objt\uv_id1 Then
			uv1 = objuv
		EndIf 
		If objuv\id = objt\uv_id2 Then
			uv2 = objuv
		EndIf 
	Next
		sv0 = AddVertex(surf,v0\x,v0\y,v0\z,uv0\u,1.0-uv0\v)
		sv1 = AddVertex(surf,v1\x,v1\y,v1\z,uv1\u,1.0-uv1\v)
		sv2 = AddVertex(surf,v2\x,v2\y,v2\z,uv2\u,1.0-uv2\v)
		AddTriangle surf,sv0,sv1,sv2
		c = c + 1
Next
UpdateNormals mesh
Return mesh
End Function 

Function saveobj(mesh,file$)
;creates a single surface mesh from an existing mesh
;and outputs it as an .obj file
;very non optimised version of the file though
;but it will come in handy when I get around to making a game
;with the other 3drt meshes that AREN'T texture the way I need
;for my games. (I need a single texture and a single surface for a 
;single shader to apply to a single material per model in raylib
;the way I do things...this is a start on that process)
If mesh = 0 Then RuntimeError("Mesh does not exist")
outfile = WriteFile(file)
If outfile = 0 Then RuntimeError("Could not save to file:"+file)
		tmp = CreateMesh()
		surf2 = CreateSurface(tmp)
		For s = 1 To CountSurfaces(mesh)
			surf = GetSurface(mesh,s)
			For tri = 0 To CountTriangles(surf) - 1
				v0 = TriangleVertex(surf,tri,0)
				v1 = TriangleVertex(surf,tri,1)
				v2 = TriangleVertex(surf,tri,2)
				;nx# = (VertexNX(surf,v0) + VertexNX(surf,v1) + VertexNX(surf,v2))*0.333
				;ny# = (VertexNY(surf,v0) + VertexNY(surf,v1) + VertexNY(surf,v2))*0.333
				;nz# = (VertexNZ(surf,v0) + VertexNZ(surf,v1) + VertexNZ(surf,v2))*0.333
				;nn# = Sqr(nx*nx+ny*ny+nz*nz)
				;If nn <> 0 Then
				;	nx = nx / nn
				;	ny = ny / nn
				;	nz = nz / nn
				;EndIf 
				;dx# = 0
				;dy# = 1
				;dz# = 0
				;dot# = dx*nx+dy*ny+dz*nz
				;dx# = -1
				;dy# = 0
				;dz# = 0
				;dot2# = dx*nx+dy*ny+dz*nz

				;If dot > -0.65 And dot2 > -0.65 Then 
					v00 = AddVertex(surf2,VertexX(surf,v0),VertexY(surf,v0),VertexZ(surf,v0),VertexU(surf,v0),VertexV(surf,v0))
					v11 = AddVertex(surf2,VertexX(surf,v1),VertexY(surf,v1),VertexZ(surf,v1),VertexU(surf,v1),VertexV(surf,v1))
					v22 = AddVertex(surf2,VertexX(surf,v2),VertexY(surf,v2),VertexZ(surf,v2),VertexU(surf,v2),VertexV(surf,v2))
					AddTriangle surf2,v00,v11,v22
				;EndIf 
			Next
		Next
		UpdateNormals tmp
		For v = 0 To CountVertices(surf2) - 1
			vx# = VertexX(surf2,v)
			vy# = VertexY(surf2,v)
			vz# = VertexZ(surf2,v)
			txt0$ = Str(vx)
			txt1$ = Str(vy)
			txt2$ = Str(vz)
			If Abs(vx)<0.001 Then txt0 = "0.0"
			If Abs(vy)<0.001 Then txt1 = "0.0"
			If Abs(vz)<0.001 Then txt2 = "0.0"
			WriteLine outfile,"v "+txt0+" "+txt1+" "+txt2
		Next
		For vt = 0 To CountVertices(surf2) - 1
			uu# = VertexU(surf2,vt)
			vv# = VertexV(surf2,vt)
			txt1 = Str(uu)
			txt2 = Str(1.0-vv)
			If Abs(uu)<0.001 Then txt1 = "0.0"
			If Abs(vv)<0.001 Then txt2 = "0.0"
			WriteLine outfile,"vt "+txt1+" "+txt2
		Next
		For vn = 0 To CountVertices(surf2) - 1
			xn# = VertexNX(surf2,vn)
			yn# = VertexNY(surf2,vn)
			zn# = VertexNZ(surf2,vn)
			txt0 = Str(xn)
			txt1 = Str(yn)
			txt2 = Str(zn)
			If Abs(xn)<0.001 Then txt0 = "0.0"
			If Abs(yn)<0.001 Then txt1 = "0.0"
			If Abs(zn)<0.001 Then txt2 = "0.0"
			WriteLine outfile,"vn "+txt0+" "+txt1+" "+txt2
		Next
		For tri = 0 To CountTriangles(surf2)-1
			v0 = TriangleVertex(surf2,tri,0) + 1
			v1 = TriangleVertex(surf2,tri,1) + 1
			v2 = TriangleVertex(surf2,tri,2) + 1
			WriteLine outfile,"f "+Str(v0)+"/"+Str(v0)+"/"+Str(v0)+" "+Str(v1)+"/"+Str(v1)+"/"+Str(v1)+" "+Str(v2)+"/"+Str(v2)+"/"+Str(v2)
		Next
		CloseFile outfile
		FreeEntity tmp
End Function 

Function texcorrectpath$(file$,texfile$)
If Instr(texfile,".\")>0 Then
	texfile = Replace(texfile,".\","")
EndIf
If Instr(texfile,"\\")>0 Then 
	texfile = Replace(texfile,"\\","\")
EndIf 
If FileType(texfile) = 1 Then Return texfile
If Instr(texfile,"\")>0 Or Instr(texfile,"/")>0 Then 
	;if this is the case it means the texfile value is something
	;weird like ".\" at the start of it....probably...
	;otherwise it should have found it....
	;if that's the case...then strip out the values up to and including 
	;the slash and then do what we do below...
	;otherwise yeah...weird file format.....
	;at least....it's not how I've usually arranged my obj files
	txt$ = ""
	For i = 1 To Len(texfile)
		If Mid(texfile,i,1)="\" Or Mid(texfile,i,1)="/" Then
			texfile = Mid(texfile,i+1,Len(texfile) - i)
			If Instr(texfile,"\")=0 And Instr(texfile,"/")=0 Then Return texfile
		EndIf 
	Next
Else
	If Instr(file,"\")>0 Or Instr(file,"/")>0 Then 
		For i = Len(file) To 1 Step - 1
			If Mid(file,i,1)="\" Or Mid(file,i,1)="/" Then
				texfile = Left(file,i) + texfile
				Return texfile
			EndIf 
		Next
	Else
		;weird that we can't find it in this case....
		;error....
	EndIf 
EndIf 
Return texfile$
End Function 

;;;;;this function is for blitz3d for my games at home to enable me to take multi surface, multi textured .obj models
;and convert them into a single surface, single texture, .obj model that I can easily load into raylib programs without
;significant changes and without having to worry about so many performance issues.

;the import routines and the export routines already exist..the only part I need to write here and upload to my website
;is the process of reducing the quantity of textures to a single texture that contains the other textures and generate
;a mesh with the right uvs for it.

;it means making a type object to store the texture names that will have to be added possibly to the import routine.
;that's easy enough to do, when you load the texture, store it in an object with both the short name and the full path.
;I'll include something similar here but it will need to modify some stuff at the PC at home when I include this...assuming
;it works as expected

Const MULTI_DEBUG = 1;

Type obj_textures
	Field shortname$
	Field fullname$
	Field index; value....numeric count of which texture this is in the list from 0 - n*m roughly
	Field imagehandle ;you'll see why we use an image not a texture later
	;don't include the handle we'll get that elsewhere
	;assume this is populated once we get to the function, by the import loader function
	;don't have to build it here
End Type

Function recordtexfile(texfile$)
	index = 0
	For ot.obj_textures = Each obj_textures
		If ot\fullname = texfile Then 
			Return
		EndIf 
		index = index + 1
	Next	
	ot.obj_textures = New obj_textures
	ot\fullname = texfile
	txt$ = texfile
	For i = Len(txt) To 1 Step - 1
		If Mid(txt,i,1) = "\" Or Mid(txt,i,1)="/" Then
			txt = Mid(txt,i+1,Len(txt)-i)
			ot\shortname = txt
			ot\index = index
			Return
		EndIf 
	Next
	ot\shortname = txt
	ot\index = index
End Function 

Function multi2singlesurface(ent)
	If ent = 0 Then Return 
	counttex = 0
	For ot.obj_textures = Each obj_textures
		counttex = counttex + 1
	Next

	;assume each texture is 512x512...we'll build it as if they are..whether or not they actually are
	;we'll be converting the textures to 512x512 anyway....
	n = Sqr(counttex)
	m = n + 1
	finaltex = CreateImage(512*n,512*m)
	mesh = CreateMesh()
	surf2 = CreateSurface(mesh)
	uu# = 0;
	vv# = 0;
	uww# = 1.0 / Float(n)
	vhh# = 1.0 / Float(m)
	ni = 0
	mi = 0
	For s = 1 To CountSurfaces(ent)
		surf = GetSurface(ent,s)
		brush = GetSurfaceBrush(surf)
		If brush<>0 Then 
			tex0 = GetBrushTexture(brush) ;not sure if syntax correct here
			If tex0<>0 Then 
				tex0name$ = TextureName(tex0)
				;look up the texture from the obj_textures list..in the shortname
				;it MUST exist - otherwise how did it get into the file?
				For ot.obj_textures = Each obj_textures
					If Instr(Lower(ot\fullname),Lower(tex0name))>0 Or Instr(Lower(tex0name),Lower(ot\fullname))>0Then 
						;yep, we've found the texture
						img = LoadImage(ot\fullname)
						ResizeImage img,512,512 ;ignore whatever size it really is
						ni = ot\index Mod n
						mi = (ot\index - ni) / n
						ux = 512 * ni
						vy = 512 * mi
						CopyRect 0,0,512,512,ux,vy,ImageBuffer(img),ImageBuffer(finaltex)
						FreeImage img
						;now go through all the triangles in this surface...and rebuild the mesh
						;with the same x/y/z coordinates but with the uvs altered as follows
						;divide them by uww and vhh, then add ni * uww and mi * vhh to their position
						For tri = 0 To CountTriangles(surf) - 1
							v0 = TriangleVertex(surf,tri,0)
							v1 = TriangleVertex(surf,tri,1)
							v2 = TriangleVertex(surf,tri,2)
							v0x# = VertexX(surf,v0)
							v0y# = VertexY(surf,v0)
							v0z# = VertexZ(surf,v0)
							v0u# = VertexU(surf,v0)
							v0v# = VertexV(surf,v0)
							v0u# = v0u# * uww + Float(ni) * uww
							v0v# = v0v# * vhh + Float(mi) * vhh
							
							
							v1x# = VertexX(surf,v1)
							v1y# = VertexY(surf,v1)
							v1z# = VertexZ(surf,v1)
							v1u# = VertexU(surf,v1)
							v1v# = VertexV(surf,v1)
							v1u# = v1u# * uww + Float(ni) * uww
							v1v# = v1v# * vhh + Float(mi) * vhh
							
							v2x# = VertexX(surf,v2)
							v2y# = VertexY(surf,v2)
							v2z# = VertexZ(surf,v2)
							v2u# = VertexU(surf,v2)
							v2v# = VertexV(surf,v2)
							v2u# = v2u# * uww + Float(ni) * uww
							v2v# = v2v# * vhh + Float(mi) * vhh
							
							;now create a bunch of new vertices and add them and their triangles to the new surf, surf2
							newv0 = AddVertex( surf2,v0x,v0y,v0z,v0u,v0v#)
							newv1 = AddVertex( surf2,v1x,v1y,v1z,v1u,v1v#)
							newv2 = AddVertex( surf2,v2x,v2y,v2z,v2u,v2v#)
							
							AddTriangle surf2,newv0,newv1,newv2
							
						Next
					EndIf 
				Next
				FreeTexture tex0
			EndIf 
			FreeBrush brush
		EndIf 
	Next
	
	SaveImage finaltex,"output_tex.bmp"
	FreeImage finaltex
	UpdateNormals mesh
	If MULTI_DEBUG<>0 Then 
		;apply the output_tex as a texture to the new mesh...and display it rotating around....
		demomesh(mesh,"output_tex.bmp")
	EndIf 
	Return mesh
End Function 

Function demomesh(ent,texfile$)
	tmpcam = CreateCamera()
	PositionEntity ent,0,0,MeshDepth(ent)*3.0
	tex = LoadTexture(texfile,1)
	If tex<>0 Then EntityTexture ent,tex
	EntityFX ent,1
	FlushKeys
	Repeat
		Cls
		TurnEntity ent,0.1,0.2,0.3
		RenderWorld
		Text 0,0,"Press Escape to Proceed, but verify the mesh looks as you expect it to look"
		Text 0,30,"Press 'x' to exit the program totally."
		Flip True
		Delay 10
		If KeyHit(45)<>0 Then End
	Until KeyHit(1)
	FreeEntity tmpcam
	FreeTexture tex
End Function

Awesome share bud!
Running a Pc that just aint. Faster nough. I7-4Ghz, 32Gb Ram, 4Gb Nvidia, 2 x 1Tb SSD's, 2 x 24" LCD's

RETRO everything!

Jesus was only famous because of his Dad

RemiD

thanks

i have coded a .obj importer / exporter for blitz3d,  years ago (in 2015 i think)

but somehow i have managed to lose it. 😅

there is aslo a obj importer / exporter in the blitzbasic code archive, but there was something incorrect, i don't remmember what, maybe duplicated vertices or maybe lack of materials.

Matty

Replace this line:

If groupid <> ogroupid And groupid > 0 Then

With these lines:
counttri = counttri + 1
If( groupid <> ogroupid And groupid > 0) or counttri>10000 Then
counttri = 0

This prevents surfaces from exceeding the vertex limit for big meshes.