hi,
i played a bit with vertexes for a cube but how can i put a texture for each side .
i know its a lame question
you must use a texture like that :
(http://courses.cs.washington.edu/courses/cse458/13au/content/exercises/images/uv/dice_texture.png)
Why?
GLB only support one texture by object ;)
I see a few ways to do so:
1. cube is one object and your textures are packed into one file (e.g. see Snoopy's suggestion)
2. make every side of your cube an object on its own and draw all six objects separately and with their own textures
3. its possible to do it with shaders, but thats a bit advanced stuff
yes Kanonet, but 1 is the best because it executes more rapidly.
I wonder how nabz32 rpg engine handle this.
I think we have multitexture command, I never try, but I think you can mix two text at the same time..
But marmor have in mind always practicallly all the engines do this mode, a unwrapped texture from the model in the 3D suite
Another way it's if you need a Cube basically do the cube for your self, it's easy with the command 3D you cand do with problems, in fact in the examples folder you have some primites and I think the cube was done in some 3D samples folder... (Only make each face separated and apply the material you want)
Use texture-atlas!
X_OBJADDVERTEX x, y, z, uv_x, uv_y, color
x,y,z = self explained
uv_x, uv_y = this is the trick
If you have a texture for example 300x300 ( to make it easy ) each sidetexture is 100x100 pixel in size so you have 3x2 textures on youre textureatlas.
uv values are from 0.0 to 1.0
so one texture in UV-Size = 1.0 / 3 = 3.3333333....
to make it clear, youre texture look like this
1 2 3
4 5 6
UV's are for the topface( number 1 ) :
Vertex 1 (topleft) : 0.0 , 0.0
Vertex 2 (bottomleft): 0.0 , 3.333
Vertex 3 (topright): 3.333, 0.0
Vertex 4 (bottomright) 3.333, 3.333
UV's are for the bottomface( number 5 ) :
Vertex 1 (topleft) : 3.333, 3.333
Vertex 2 (bottomleft): 3.333 , 6.666
Vertex 3 (topright): 6.666, 3.333
Vertex 4 (bottomright) 6.666, 6.666
and so on...
Quote from: kanonet on 2014-Apr-23
2. make every side of your cube an object on its own and draw all six objects separately and with their own textures
Actually only one of the same object is needed of a cube face. :whistle: Then push and pop matrix to where the facet is rotated and independent texture map sprites can be applied without uv and three times higher the resolution for a fixed sprite map size. :good: Memory is the main issue here really. :-[
For my Terrain I created one object per square and use a single Texture on every square.
I also use multi texturing through different UV mapping, i e. when using Auto connecting textures like ways etc. the correct tile is selected by altering the UV data of the plain ( Walls are also multi textured by different UV coordinates, where one Wall object has 4 Polys ).
I also do the "Animation" of the grass by altering the UV coordinates.
It would also be possible to draw the entire visible Terrain including walls as one object and set the UV coordinates accordingly.
With this, a new bmp has to be created at the start of each Level, containing all the tile Textures in the Tileset used for this Level ( and this BMP has to be a square. ).
I think drawing every Side of a cube 6 times is always Overkill.
It means that you call Draw orders for at least 3 sides, which aren“t visible.
Also calling all the rotations is more math than simply use the fixed coordinates provided by a mesh.
Also Splitting a texture by using different UV coordinates gets you a more Slim code.
I agree with the venerable master nabz32 :nw:
thx a lot for your Hints :booze:
@ Schranzor 1/3 = 3.3333 ? :x :whistle:
Yes, a texture atlas / sprite sheet, and manually map the UV coordinates.
(Or create your 3d game object in a modeling program and import it into your game.)
I have some library code that can assist you. It wont run as is (like my usual code snippets) because it depends on other TYPEs from my other library modules.
For starters, here's my dynamic mesh TYPE:
//-------------------------------------------------------------------------- U V
//FUNCTION _______TUv:
//ENDFUNCTION
TYPE TUv
u#
v#
FUNCTION Set: u#, v#
self.u = u
self.v = v
ENDFUNCTION
FUNCTION Dump$:
LOCAL rv$
rv$ = "(" + FormatFlt$(self.u, 6, 2) + "," + FormatFlt$(self.v, 6, 2) + ")"
RETURN rv$
ENDFUNCTION
ENDTYPE
//---------------------------------------------------------------- V e r t i c e
//FUNCTION _______TVertice:
//ENDFUNCTION
TYPE TVertice
x
y
z
colour%
uv AS TUv
FUNCTION Dump$:
LOCAL rv$=""
rv$ = "[v: (" + (self.x) + "," + (self.y) + "," + (self.z) + ") "
rv$ = rv$ + "rgb:" + FormatColour$(self.colour) + " "
rv$ = rv$ + "uv:" + self.uv.Dump$() + "]"
RETURN rv$
ENDFUNCTION
FUNCTION Set: x, y, z, colour%, u=0.0, v=0.0
self.x = x
self.y = y
self.z = z
self.colour = colour
self.uv.Set(u, v)
ENDFUNCTION
FUNCTION Clear%:
self.x = BLANK
self.y = BLANK
self.z = BLANK
self.colour = BLANK
self.uv.Set(0.0, 0.0)
ENDFUNCTION
FUNCTION Adjust%: x#, y#, z#
INC self.x, x
INC self.y, y
INC self.z, z
ENDFUNCTION
ENDTYPE
//---------------------------------------------------------------------- Q u a d
//FUNCTION _______TQuad:
//ENDFUNCTION
TYPE TQuad
v1 AS TVertice
v2 AS TVertice
v3 AS TVertice
v4 AS TVertice
FUNCTION Dump$:
LOCAL rv$=""
rv$ = " v1:" + self.v1.Dump$() + "\n"
rv$ = rv$ + " v2:" + self.v2.Dump$() + "\n"
rv$ = rv$ + " v3:" + self.v3.Dump$() + "\n"
rv$ = rv$ + " v4:" + self.v4.Dump$()
RETURN rv$
ENDFUNCTION
FUNCTION Clear%:
self.v1.Clear()
self.v2.Clear()
self.v3.Clear()
self.v4.Clear()
ENDFUNCTION
FUNCTION Set%: v1 AS TVertice, v2 AS TVertice, v3 AS TVertice, v4 AS TVertice
self.v1 = v1
self.v2 = v2
self.v3 = v3
self.v4 = v4
ENDFUNCTION
FUNCTION Set%: v1 AS TVertice, v2 AS TVertice, v3 AS TVertice, v4 AS TVertice, uv1 AS TUv, uv2 AS TUv, colour%=BLANK
self.v1 = v1
self.v2 = v2
self.v3 = v3
self.v4 = v4
self.SetUv(uv1, uv2)
IF (colour <> BLANK) THEN self.ColourSet(colour)
ENDFUNCTION
FUNCTION Adjust%: x#, y#, z#
self.v1.Adjust(x, y, z)
self.v2.Adjust(x, y, z)
self.v3.Adjust(x, y, z)
self.v4.Adjust(x, y, z)
ENDFUNCTION
FUNCTION Copy AS TQuad:
LOCAL dest AS TQuad
dest.Clear()
dest.v1 = self.v1
dest.v2 = self.v2
dest.v3 = self.v3
dest.v4 = self.v4
RETURN dest
ENDFUNCTION
FUNCTION SetUv%: uv1 AS TUv, uv2 AS TUv
self.v1.uv.u = uv1.u; self.v1.uv.v = uv1.v
self.v2.uv.u = uv2.u; self.v2.uv.v = uv1.v
self.v3.uv.u = uv2.u; self.v3.uv.v = uv2.v
self.v4.uv.u = uv1.u; self.v4.uv.v = uv2.v
ENDFUNCTION
FUNCTION ColourSet%: colour%
self.v1.colour = colour
self.v2.colour = colour
self.v3.colour = colour
self.v4.colour = colour
ENDFUNCTION
ENDTYPE
//---------------------------------------------------------------------- M e s h
//FUNCTION ________MESH:
//ENDFUNCTION
TYPE TMesh
id% = -1
quads[] AS TQuad
FUNCTION Dump$: name$
LOCAL qx%
LOG("TMesh: [" + name$ + "], id:[" + self.id + "]")
FOR qx = 0 TO LEN(self.quads[]) - 1
LOG(" > Q" + (qx+1) + ": " + self.quads[qx].Dump$(), TRUE, FALSE)
NEXT
ENDFUNCTION
FUNCTION Clear:
IF self.id < 0 THEN self.id = GENX_OBJ()
DIM self.quads[0]
ENDFUNCTION
FUNCTION Load: fn$
LOG("TMesh.LoadModel: [" + GETCURRENTDIR$() + fn$ + "]")
IF File_Exists(fn$)
X_LOADOBJ fn$, self.id
ELSE
LOG("** ERROR ** Model file doesn't exist: [" + fn$ + "]")
ENDIF
ENDFUNCTION
// 5-----4
// /| . /|
// / | /.|
// 0-----1 7
// | /. | /
// |/ |/
// 3-----2
FUNCTION NewCube: width, height, depth, colour%, uv1 AS TUv, uv2 AS TUv
LOCAL w, h, d
LOCAL vx[] AS TVertice
LOCAL c2%
c2 = Colour_Brightness(colour, -85)
DIM vx[8]
w = width / 2.0
h = height / 2.0
d = depth / 2.0
vx[0].Set(-w, h, d, colour) // FTL
vx[1].Set( w, h, d, colour) // FTR
vx[2].Set( w,-h, d, colour) // FBR
vx[3].Set(-w,-h, d, colour) // FBL
vx[4].Set( w, h,-d, c2) // BTR
vx[5].Set(-w, h,-d, c2) // BTL
vx[6].Set(-w,-h,-d, c2) // BBL
vx[7].Set( w,-h,-d, c2) // BBR
self.QuadAddV4(vx[0], vx[1], vx[2], vx[3], uv1, uv2) // Front
self.QuadAddV4(vx[5], vx[0], vx[3], vx[6], uv1, uv2) // Left
self.QuadAddV4(vx[1], vx[4], vx[7], vx[2], uv1, uv2) // Right
self.QuadAddV4(vx[5], vx[4], vx[1], vx[0], uv1, uv2) // Top
self.QuadAddV4(vx[3], vx[2], vx[7], vx[6], uv1, uv2) // Bottom
self.QuadAddV4(vx[4], vx[5], vx[6], vx[7], uv1, uv2) // Back
self.Generate()
DIM vx[0]
ENDFUNCTION
FUNCTION QuadAdd%: quad AS TQuad
DIMPUSH self.quads[], quad
ENDFUNCTION
FUNCTION QuadAddV4%: v1 AS TVertice, v2 AS TVertice, v3 AS TVertice, v4 AS TVertice
LOCAL quad AS TQuad
quad.Set(v1, v2, v3, v4)
DIMPUSH self.quads[], quad
ENDFUNCTION
FUNCTION QuadAddV4%: v1 AS TVertice, v2 AS TVertice, v3 AS TVertice, v4 AS TVertice, uv1 AS TUv, uv2 AS TUv
LOCAL quad AS TQuad
quad.Set(v1, v2, v3, v4, uv1, uv2)
DIMPUSH self.quads[], quad
ENDFUNCTION
FUNCTION Generate%:
LOCAL qx%
LOCAL qty%
qty = LEN(self.quads[]) - 1
X_OBJSTART self.id
FOR qx = 0 TO qty
self.QuadGenerate(qx)
IF qx < qty THEN X_OBJNEWGROUP
NEXT
X_OBJEND
ENDFUNCTION
FUNCTION QuadGenerate: qx%, cullmode%=CULLMODE_FRONT
ALIAS quad AS self.quads[qx]
IF (cullmode = CULLMODE_BACK) OR (cullmode = CULLMODE_BOTH)
X_OBJADDVERTEX quad.v1.x, quad.v1.y, quad.v1.z, quad.v1.uv.u, quad.v1.uv.v, quad.v1.colour
X_OBJADDVERTEX quad.v4.x, quad.v4.y, quad.v4.z, quad.v4.uv.u, quad.v4.uv.v, quad.v4.colour
X_OBJADDVERTEX quad.v2.x, quad.v2.y, quad.v2.z, quad.v2.uv.u, quad.v2.uv.v, quad.v2.colour
X_OBJADDVERTEX quad.v3.x, quad.v3.y, quad.v3.z, quad.v3.uv.u, quad.v3.uv.v, quad.v3.colour
ENDIF
IF (cullmode = CULLMODE_FRONT) OR (cullmode = CULLMODE_BOTH)
quad.Adjust(0, -0.0001, 0)
X_OBJADDVERTEX quad.v2.x, quad.v2.y, quad.v2.z, quad.v2.uv.u, quad.v2.uv.v, quad.v2.colour
X_OBJADDVERTEX quad.v3.x, quad.v3.y, quad.v3.z, quad.v3.uv.u, quad.v3.uv.v, quad.v3.colour
X_OBJADDVERTEX quad.v1.x, quad.v1.y, quad.v1.z, quad.v1.uv.u, quad.v1.uv.v, quad.v1.colour
X_OBJADDVERTEX quad.v4.x, quad.v4.y, quad.v4.z, quad.v4.uv.u, quad.v4.uv.v, quad.v4.colour
ENDIF
RETURN
ENDFUNCTION
ENDTYPE
You could hand code your UV locations, as calculated by Schranz0r, or edit your sprite sheet in 'darkFunction Editor' (http://darkfunction.com/editor/ (http://darkfunction.com/editor/)). Use this tool to layout your individual sprites (each face of the cube for your example), give each side a name ('side1', 'side2', etc), then export the definitions to an xml file.
Use the following code (again, probably doesn't run out of the box) to read and parse this xml file.
First, define a variable of type TSpriteSheet, and use it like this:
LOCAL spriteSheet AS TSpriteSheet
spriteSheet.Load("", "sprites.xml") // Assuming in current directory ("")
// You can now find each defined sprite like this:
LOCAL side1 AS TSprite
side1 = spriteSheet.Find("side1") // Returns 'side1' index in TSpriteSheet's 'sprites[]' array
Here's the TSpriteSheet library code:
TYPE TSpriteSheet
fn_image$ // Filename of image
id_sprite% // Sprite ID of image
size AS TSize // Size of image
sprites[] AS TSprite
FUNCTION Load%: path$, fn$
LOCAL ix%
LOCAL fh% // File Handle
LOCAL line$
LOCAL tags$[]
LOCAL key$
LOCAL value$
LOCAL name$
LOCAL sprite AS TSprite
self.fn_image$ = ""
self.id_sprite = 0
self.size.Set(0,0)
DIM self.sprites[0]
path$ = TRIM$(path$)
fh = GENFILE()
IF OPENFILE(fh, path$ + fn$, 1) = FALSE // Open SpriteSheet file
LOG(">TSpriteSheet.Load(): [" + fn$ + "] -> Error opening file")
RETURN FALSE // Exit and return 'FALSE' if can't open
ENDIF
WHILE ENDOFFILE(fh) = FALSE
READLINE fh, line$ // Read next line of data for the next glyph/character
line$ = TRIM$(line$)
line$ = MID$(line$, 1) // Strip off first '<' character
line$ = LEFT$(line$, LEN(line$) - 1) // Strip off last '>' character
IF RIGHT$(line$, 1) = "/" THEN line$ = LEFT$(line$, LEN(line$) - 1) // Strip off last '/' character if present
SPLITSTR(line$, tags$[], " ", TRUE)
IF LEN(tags$[]) < 1 THEN CONTINUE // Ignore elements with no tags
tags$[0] = TRIM$(UCASE$(tags$[0]))
SELECT tags$[0]
CASE "DIR"
// Image FileName
String_KeyValue(tags$[1], key$, value$)
value$ = UCASE$(TRIM$(value$))
IF value$ <> "/" THEN name$ = name$ + value$
name$ = name$ + "/"
CASE "/DIR"
ix = REVINSTR(name$, "/", LEN(name$)-2)
IF ix >= 0
name$ = LEFT$(name$, ix+1)
ENDIF
CASE "IMG"
// Image FileName
String_KeyValue(tags$[1], key$, value$)
self.fn_image$ = path$ + value$
// Image Width
String_KeyValue(tags$[2], key$, value$)
self.size.w = INTEGER(value$)
// Image Height
String_KeyValue(tags$[3], key$, value$)
self.size.h = INTEGER(value$)
LOG(" >IMG: fn:[" + self.fn_image$ + "] w:[" + self.size.w + "] h:[" + self.size.h + "]")
CASE "SPR"
// Sprite Name
String_KeyValue(tags$[1], key$, value$)
sprite.name$ = name$ + UCASE$(value$)
// Sprite X
String_KeyValue(tags$[2], key$, value$)
sprite.uv.x1 = INTEGER(value$)
// Sprite Y
String_KeyValue(tags$[3], key$, value$)
sprite.uv.y1 = INTEGER(value$)
// Sprite W
String_KeyValue(tags$[4], key$, value$)
sprite.uv.x2 = sprite.uv.x1 + INTEGER(value$)
// Sprite H
String_KeyValue(tags$[5], key$, value$)
sprite.uv.y2 = sprite.uv.y1 + INTEGER(value$)
sprite.colour = RGB(255,255,255)
DIMPUSH self.sprites[], sprite
LOG(" >SPR: name:[" + sprite.name$ + "] xywh:" + sprite.uv.Dump$())
ENDSELECT
WEND
CLOSEFILE fh // Close '.fnt' file
self.id_sprite = Sprite_Load(self.fn_image$, FALSE)
ENDFUNCTION
FUNCTION PolyStart:
STARTPOLY self.id_sprite, POLYMODE_STRIP
ENDFUNCTION
FUNCTION PolyEnd:
ENDPOLY
ENDFUNCTION
FUNCTION Find%: name$
LOCAL sx%
name$ = UCASE$(TRIM$(name$))
FOR sx = 0 TO LEN(self.sprites[]) - 1
IF INSTR(self.sprites[sx].name$, name$) >= 0
RETURN sx
ENDIF
NEXT
RETURN 0
ENDFUNCTION
FUNCTION Draw%: id%, x%, y%, w%=-1, h%=-1, colour%=BLANK, stretch%=TRUE, scale#=1.0
IF id < 0 THEN RETURN
IF id > LEN(self.sprites[])-1 THEN RETURN
self.sprites[id].Draw(x, y, w, h, colour, stretch, scale)
ENDFUNCTION
FUNCTION CalcUv AS TUv: id_sprite%, is_top_left%=TRUE
LOCAL uv AS TUv
LOCAL sw#, sh#
sw = self.size.w
sh = self.size.h
IF is_top_left
uv.Set(self.sprites[id_sprite].uv.x1 / sw, self.sprites[id_sprite].uv.y1 / sh)
ELSE
uv.Set(self.sprites[id_sprite].uv.x2 / sw, self.sprites[id_sprite].uv.y2 / sh)
ENDIF
RETURN uv
ENDFUNCTION
ENDTYPE
TYPE TSprite
name$
uv AS TXyXy
colour%
ENDTYPE
TYPE TXyXy
x1%
y1%
x2%
y2%
FUNCTION Set: x1%, y1%, x2%, y2%
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
ENDFUNCTION
FUNCTION Clear:
self.x1 = 0
self.x2 = 0
self.y1 = 0
self.y2 = 0
ENDFUNCTION
ENDTYPE
My 'TMesh' TYPE has a 'NewCube()' function you could use, except each face would use the same graphic.
(Although you code still do this for it to calculate the proper 3d x,y,z vertice locations, then go back and set the proper UVs afterwards.)
You should calculate each cube face's UVs, and call the .AddQuad() command to add the quad to your model.
Here's some basic tips on how you can use the above libraries for your situation:
LOCAL cube AS TMesh
LOCAL v1, v2, v3, v4 AS TVertice
cube.Clear()
// Define each face by specifying each corner's 3d location, plus UV location:
// TOP FACE
v1.Set(x, y, z, colour, u, v);
v2.Set(x, y, z, colour, u, v);
v3.Set(x, y, z, colour, u, v);
v4.Set(x, y, z, colour, u, v);
// Or get the u,v data from the sprite sheet above:
// spriteSheet.sprites[side1].uv.x1
cube.QuadAddV4(v1, v2, v3, v4);
// . . . repeat for all 6 faces
// Finally, generate the actual 3d model:
cube.Generate();
//Then use 'cube.id' to reference this model when displaying:
X_DRAWOBJ cube.id, 0
[Edit] Code tweaking and formatting, etc. I would whip up a simple project except I no longer have GLBasic installed anywhere. I could just zip up my old maze project which used these libraries religiously so you can see how I do it (most objects dynamically generated). That would include all my libraries in case I missed any. But the project is quite large and may be difficult to follow or understand. Plus it has been abandoned for over a year, and may not even be in a running state. It's a shame all that work went into creating my libraries and now it is abandoned! ha, that's life for ya.