create a cube with diff. textur on each side

Previous topic - Next topic

Marmor

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

SnooPI

you must use a texture like that :


SnooPI

Why?

GLB only support one texture by object  ;)

kanonet

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
Lenovo Thinkpad T430u: Intel i5-3317U, 8GB DDR3, NVidia GeForce 620M, Micron RealSSD C400 @Win7 x64

SnooPI

yes Kanonet, but 1 is the best because it executes more rapidly.

Marmor

I wonder how nabz32 rpg engine handle this.


mentalthink

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)

Schranz0r

#7
Use texture-atlas!


Code (glbasic) Select
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...
I <3 DGArray's :D

PC:
AMD Ryzen 7 3800X 16@4.5GHz, 16GB Corsair Vengeance LPX DDR4-3200 RAM, ASUS Dual GeForce RTX™ 3060 OC Edition 12GB GDDR6, Windows 11 Pro 64Bit, MSi Tomahawk B350 Mainboard

matchy

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.  :-[

nabz32

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.











SnooPI

I agree with the venerable master nabz32  :nw:

Marmor

thx a lot for your Hints  :booze:

@ Schranzor     1/3 = 3.3333 ?  :x   :whistle:

Slydog

#12
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:
Code (glbasic) Select
//-------------------------------------------------------------------------- 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/).  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:

Code (glbasic) Select
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:
Code (glbasic) Select
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:
Code (glbasic) Select
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.
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]