When i did this, i created my own format for extracting and coding data in the Name of each entity. This is the code i use to load in my level and parse the string of the EntityName(). I copy this data to another string, and NAME the child entity with a NUMBER. This number is a handle i can use to instantly get the entity, without searching. You don't really need to know about that, so for now just ignore the line that says:
NameEntity e\ent, Handle(e.entity) ; store the type object the entity is held in, in the entity name. Allows easy
; access to the type object if you only have the mesh handle.
I first set up a type collection to store every piece of data i will be extracting, such as:
Type entity
Field ent
Field x,y,z
Field name$
Field event ; 0 = none, 1 = entity move. These are ENTITY EVENTS!
Field ex#,ey#,ez# ; event modifiers
Field cubemap ; set to zero for no texture. Set to 1 for update everyframe. Set to 2 for once only update
Field texture[7] ; texture 4 is the location of cubemap textures.
Field texture_px#[7]
Field texture_py#[7]
Field texture_sx#[7]
Field texture_sy#[7]
Field texture_rot#[7]
Field texture_blend%[7] ; holds the different blends for the textures
Field texture_event%[7] ; holds the value of a texture event
Field texture_cube%[7] ; whether cubemap is active. 0 = no cubemap. 1 - 3 set cubemode
Field EntBlend%
Field EntType%
Field EntPick%
Field fx%
Field event_handle
End Type
The cubemap field needs updating to an array, because i'm assuming only one cubemap per entity, but you could have more than one. ANYWAY, as you see, i'm storing alot of information about each child entity. NOW, these fields regarding textures:
Field texture[7] ; stores the handle of the texture.
Field texture_px#[7]
Field texture_py#[7]
Field texture_sx#[7]
Field texture_sy#[7]
Field texture_rot#[7]
Field texture_blend%[7] ; holds the different blends for the textures
Field texture_event%[7] ; holds the value of a texture event
Basically, in the 3d world editor, i would say, put as a name:
name=wateredge texture_event1=1 tx1=0 ty1=0.005 entitypick=0 entitytype=0 entityblend=3
Now, my code would hunt through this, searching for each occurance of the following: (just a snippet of the code)
NameEntity temp\ent,temp_info$
temp\name = temp_info$
DebugLog("name="+temp_info$+"|")
temp\event = extract(parse,"eventtype=")
DebugLog("eventtype = "+temp\event)
temp\ex = extract(parse,"ex=")
DebugLog("ex = "+temp\ex)
temp\ey = extract(parse,"ey=")
DebugLog("ey = "+temp\ey)
temp\ez = extract(parse,"ez=")
DebugLog("ez = "+temp\ez)
There is more code to go through the individual texture layers and search for texture event properties:
For texture_event_loop = 0 To 7
temp_string$ = "texture_event"+texture_event_loop+"="
temp\texture_event[texture_event_loop] = extract(parse,temp_string)
DebugLog(temp_string+" "+temp\texture_event[texture_event_loop])
temp_string$ = "tx"+texture_event_loop+"="
temp\texture_px[texture_event_loop] = extract(parse,temp_string)
DebugLog(temp_string+" "+temp\texture_px[texture_event_loop])
temp_string$ = "ty"+texture_event_loop+"="
temp\texture_py[texture_event_loop] = extract(parse,temp_string)
DebugLog(temp_string+" "+temp\texture_py[texture_event_loop])
Next
plugs the loop value into the type field arrays (they can only be one dimensional btw and can't be resized i think?)
The debuglog is purely so i can check it is parsing the information correctly. I have a function that you pass across a string too, and text to search for. It will search for the text, then search for an "=" sign straight after, then a number, and will stop when it finds a space, so the format has to be perfect, ie:
texture_event1=1
would work fine. Anyway, the function returns the value of that property. So, when your finished, your type collection may look something like this:
(assuming you only had 2 texture layers that is, 0 and 1)
Field ent = handle of the entity
Field x=10, y=20, z=70
Field name$ = "waterfall"
Field texture_event(0) = 0 ; 0 = none, 1 = texture move
Field texture_px(0) = 0 ; texture modifier values
Field texture_py(0) = 0 ; texture modifier values
Field texture_event(1) = 1 ; 0 = none, 1 = texture move
Field texture_px(1) = 0
Field texture_py(1) = 0
(Please note as this point the other texture fields "texture_rx()"," texture_ry()" etc etc, mean nothing just now, as my system doesn't use them yet, and i'll probably do away with them, unless i find i need for a moving AND rotating AND scaling texture, all at once :o))
Now, when parsing each entity, the code checks the event field and the texture_event fields, generated when reading the "texture_event1=1" text from the parsed text. In this case the texture_event1 is 1, so the event field is set to 1. The code reads this, and generates a new type object for the EVENT type collection:
Type texture_event
Field once ; 0 = do for ever, 1 = do once
Field event_number ; 1 = texture move, 2 = texture rotate, 20 = cubemap update
Field x#,y#
Field entity_handle
Field texture ; handle for a texture if an event needs a dyanmically created texture.
Field texture_transform ; the texture number to moved/rotated/scaled... etc etc
End Type
My code will is (this is an old version) a long list of IF/CASE SELECT statements, evaluating the texture_event passed across to the function:
For example:
If texture_event1 = 1 then
;set up event for texture move
ElseIf texture_event1 = 2 then
;set up event for texture rotate
etc etc
Now, you may have noticed above the texture_px1,py1 and ez fields. These are the actualy amounts the events will be transformed by. So for the texture moving, i have:
name=wateredge texture_event1=1 tx1=0 ty1=0.005 entitypick=0 entitytype=0 entityblend=3
texture_px(1)= 0 and texture_py(1) = 0.05
the newly created event type object will take these numbers, and it will copy them into the event, so your event object will end up looking like this: (the numbers for the handles are just randomly made up by me :o) blitz produces them on loading entities/textures )
Field once = 1 ; since it wasn't specified, it will default to 1(do forever)
Field event_number = 1 ; meaning move texture
Field x#=0,y#=0.05
Field entity_handle = 25627738 ; the handle blitz assigns when loading, to keep track of it. this is so you can easily get the texture
Field texture = 25678383; handle for a texture if an event needs a dyanmically created texture.
Field texture_transform = 1 our data provided a texture layer to be transformed, in this case moved, and it was texture layer 1
So, every frame, in the main game, this will be performed, synced with the frame rate of course.
Here is the full code. It's a little out of date with regards to texture_events and the event generating code. Can't seem to find my new version... but i hope this has given you a basic idea. You really need to write your own though, as it can be hard to read someone elses code and emulate what they did. I'm sure you'll even make it more readable too!
; Level Setup Include file. This include will setup some basic scenary to test out the kid and game
; features. This will be changed in time, so this is a temperary fix :o)
Global world_pivot = CreatePivot()
Global lightmap_main
Type entity
Field ent
Field x,y,z
Field name$
Field event ; 0 = none, 1 = texture move
Field ex#,ey#,ez# ; event modifiers
Field cubemap ; set to zero for no texture. Set to 1 for update everyframe. Set to 2 for once only update
Field texture[7] ; texture 4 is the location of cubemap textures.
Field texture_px#[7]
Field texture_py#[7]
Field texture_sx#[7]
Field texture_sy#[7]
Field texture_rot#[7]
Field texture_blend%[7] ; holds the different blends for the textures
Field texture_event%[7] ; holds the value of a texture event
Field texture_cube%[7] ; whether cubemap is active. 0 = no cubemap. 1 - 3 set cubemode
Field EntBlend%
Field EntType%
Field EntPick%
Field fx%
Field event_handle
End Type
Type event
Field once ; 0 = do for ever, 1 = do once
Field event_number ; 0 = none in which case why's it here? 1 = texture move, 20 = cubemap update
Field x#,y#,z#
Field entity_handle
Field texture ; handle for a texture if an event needs a dyanmically created texture.
Field texture_transform ; the texture number to moved/rotated/scaled... etc etc
End Type
Global level1a = LoadAnimMesh("levels\level1\copy of testlevel.b3d")
PositionEntity level1a,0,-400,-100
ScaleEntity level1a,3,3,3
EntityPickMode level1a,2
EntityType level1a,2
test_children = CountChildren(level1a)
DebugLog("number of children = "+test_children)
Global water ; used for test cubemaps
Global water_edge ; as above
Global count_z = 0
For loop = 1 To test_children
e.entity = New entity
e\ent = GetChild(level1a,loop)
DebugLog(" Child "+loop+" - Name: "+EntityName (e\ent))
EntityPickMode e\ent,2
EntityType e\ent,2
e\x = EntityX(e\ent,True)
e\y = EntityX(e\ent,True)
e\z = EntityX(e\ent,True)
parse_entity_name(e.entity)
DebugLog(" straight out of the function, name = "+e\name)
NameEntity e\ent, Handle(e.entity) ; store the type object the entity is held in, in the entity name. Allows easy
; access to the type object if you only have the mesh handle.
If e\name = "water" Then
water = e\ent
End If
If e\name = "wateredge" Then
water_edge = e\ent
End If
parse_entity_textures(e.entity)
; set blend modes of textures
For texture_loop = 0 To 7
If e\texture[texture_loop] <> 0 Then
DebugLog(" texture exists - "+texture_loop+" - "+e\texture[texture_loop])
DebugLog(" texture_path - "+TextureName(e\texture[texture_loop]))
If e\texture_blend[texture_loop] <> 0 Then
TextureBlend e\texture[texture_loop] , e\texture_blend[texture_loop]
DebugLog(" TextureBlend e\texture["+e\texture[texture_loop]+"] , "+e\texture_blend[texture_loop])
Else
DebugLog(" But no blend mode exists")
DebugLog( e\texture_blend[texture_loop])
End If
End If
Next
; parse events
parse_events(e.entity)
;EntityType e\ent,2
;EntityPickMode e\ent,2
DebugLog(" -----------------")
Next
Function parse_events(temp.entity)
;parse texture events
For texture_event_loop = 0 To 7
If temp\texture_event[texture_event_loop] = 1 ; move texture
DebugLog(" MOVE TEXTURE EVENT FOUND!")
ev.event = New event
ev\event_number = temp\texture_event[texture_event_loop]
ev\once = 0 ; texture keeps moving forever
ev\x = temp\texture_px[texture_event_loop]
ev\y = temp\texture_py[texture_event_loop]
ev\z = 0
ev\entity_handle = Handle(temp.entity) ;-store the handle to this type object in the event type object.
; This allows the event to access and change the entity/textures
ev\texture_transform = texture_event_loop
DebugLog(" ev\event_number = "+ev\event_number)
DebugLog(" ev\x = "+ev\x)
DebugLog(" ev\y = "+ev\y)
DebugLog(" temp\texture_px(texture_event_loop) = "+temp\texture_px[texture_event_loop])
DebugLog(" temp\texture_py(texture_event_loop) = "+temp\texture_py[texture_event_loop])
; ElseIf temp_texture_event[texture_event_loop] = 10 Then ; cubemap
End If
Next
; If temp\event = 1 Then ; move texture event
; ev\event = New event
; ev\event_number = temp\event
;
; If e\event = 1 Then
; ev.event = New event
; ev\event_number = e\event
; ev\once = 0
; ev\x = e\ex
; ev\y = e\ey
; ev\z = e\ez
; ev\entity_handle = Handle(e.entity) ;-store the handle to this type object in the event type object.
; ; This allows the event to access it and change the entity/textures
; e\event_handle = Handle(ev.event) ;-Similarly store the handle to the event in the entity type object.
; End If
;
; If e\cubemap = 2 Then ; cubemap = 2 means only do once
;
; ev.event = New event
; ev\event_number = 20 ; 20 for cubemap update. << special update so gets a number hardcoded
; ev\once = 1
; ev\entity_handle = Handle(e.entity)
; e\texture[4] = CreateTexture(128,128,1+2+128+256) ; create cubemap texture
; EntityTexture e\ent,e\texture[4],0,4
; TextureBlend e\texture[4],2
; SetCubeMode e\texture[4],3
; End If
End Function
Function parse_entity_textures(temp.entity)
surfs = CountSurfaces(temp\ent)
For loop = 1 To surfs
t_surf = GetSurface(temp\ent,loop)
t_brush = GetSurfaceBrush(t_surf)
If t_brush <> 0 Then
For index = 0 To 7
DebugLog(" extracting textures")
temp\texture[index] = GetBrushTexture(t_brush,index)
DebugLog(" texture path: "+TextureName$(temp\texture[index]))
; If TextureName(temp\texture[index]) <> "" Then
; If temp\texture_cube[index] > 0 Then
; temp\texture[index] = LoadTexture(TextureName(temp\texture[index]),128)
; End If
; End If
If temp\texture_cube[index] > 0 Then
SetCubeMode temp\texture[index],temp\texture_cube[index]
DebugLog("setting cube mode to "+temp\texture_cube[index]+"#################################################################################")
End If
If index = 3 Then
If temp\texture[index] = 0 Then
;DebugLog("No lightmap exists!")
Else
DebugLog("Lightmap in place: "+temp\texture[index])
lightmap_main = temp\texture[index]
End If
End If
; I have commented out the below lines, as these will now be used as event variables for the textures
;temp\texture_sx[index] = 0
;temp\texture_sy[index] = 0
;temp\texture_rot[index] = 0
;temp\texture_px[index] = 0
;temp\texture_py[index] = 0
Next
Exit
End If
Next
End Function
Function parse_entity_name(temp.entity)
parse$ = EntityName(temp\ent)
temp_info$ = extract(parse,"name=")
If temp_info$ = "0" Then
NameEntity temp\ent,"none"
DebugLog("name="+EntityName(temp\ent))
EntityFX temp\ent,0
Else
NameEntity temp\ent,temp_info$
temp\name = temp_info$
DebugLog("name="+temp_info$+"|")
temp\event = extract(parse,"eventtype=")
DebugLog("eventtype = "+temp\event)
temp\ex = extract(parse,"ex=")
DebugLog("ex = "+temp\ex)
temp\ey = extract(parse,"ey=")
DebugLog("ey = "+temp\ey)
temp\ez = extract(parse,"ez=")
DebugLog("ez = "+temp\ez)
For texture_event_loop = 0 To 7
temp_string$ = "texture_event"+texture_event_loop+"="
temp\texture_event[texture_event_loop] = extract(parse,temp_string)
DebugLog(temp_string+" "+temp\texture_event[texture_event_loop])
temp_string$ = "tx"+texture_event_loop+"="
temp\texture_px[texture_event_loop] = extract(parse,temp_string)
DebugLog(temp_string+" "+temp\texture_px[texture_event_loop])
temp_string$ = "ty"+texture_event_loop+"="
temp\texture_py[texture_event_loop] = extract(parse,temp_string)
DebugLog(temp_string+" "+temp\texture_py[texture_event_loop])
Next
temp\cubemap = extract(parse,"cubemap=")
DebugLog("cubemap= "+temp\cubemap)
temp\EntPick = extract(parse,"entitypick=")
EntityPickMode temp\ent, temp\EntPick
DebugLog("EntityPick = "+temp\EntPick)
temp\EntType = extract(parse,"entitytype=")
EntityType temp\ent, temp\EntType
DebugLog("EntityType = "+temp\EntType)
temp\EntBlend = extract(parse,"entityblend=")
If temp\EntBlend = 0 Then temp\EntBlend = 1
EntityBlend temp\ent,temp\EntBlend
DebugLog("EntityBlend= "+temp\EntBlend)
temp\fx = extract(parse,"fx=")
EntityFX temp\ent,temp\fx
DebugLog("EntityBlend= "+temp\fx)
For texture_blend_check = 0 To 7
temp_string$ = "textureblend"+texture_blend_check+"=" ; build string data for parse via loop value
temp\texture_blend[texture_blend_check] = extract(parse,temp_string$)
DebugLog("TextureBlend ["+texture_blend_check+"] = "+temp\texture_blend[texture_blend_check])
Next
For texture_cube_check = 0 To 7
temp_string$ = "texturecube"+texture_cube_check+"=" ; build string data for parse via loop value
temp\texture_cube[texture_cube_check] = extract(parse,temp_string$)
DebugLog("Texturecube ["+texture_cube_check+"] = "+temp\texture_cube[texture_cube_check])
Next
EntityFX temp\ent,0 ; only use temp thus far. Trying ambient light out.
End If
End Function
; parameter option indicates whether it is a string (0), an integer (1) or a float (2)
; Small problem in the function. Fixed it i hope. Because it looks for a space " " at the end of the data,
; so it knows what to extract, the data at the very end of the parse text, isn't picked up. No errors were
; generated because it went right through the second loop. Now i will just take everything to the last character
; in the string.
Function extract$(parse$,search$)
If Len(parse) < Len(search$) Then
;DebugLog(" ERROR! Search is bigger than string.")
;DebugLog(" parsing : "+parse)
;DebugLog(" searching for : "+search)
Return "0"
Else
For temp_loop = 1 To Len(parse)
If Mid$(parse,temp_loop,Len(search$)) = search$ Then
temp_text$ = Mid$(parse,temp_loop,Len(search$))
temp_pos = temp_loop + Len(search)
Exit
End If
Next
If Len(search$) = Len(temp_text$) Then
For temp_loop = temp_pos To Len(parse)
If Mid$(parse,temp_loop,1) = " " Then
temp_temp$ = Mid$(parse,temp_pos,temp_loop-temp_pos)
DebugLog("length of string passed back = "+(temp_loop-temp_pos))
Return Mid$(parse,temp_pos,temp_loop-temp_pos)
End If
Next
temp_temp$ = Mid$(parse,temp_pos,temp_loop-temp_pos)
DebugLog("length of string passed back = "+(temp_loop-temp_pos))
Return Mid$(parse,temp_pos,temp_loop-temp_pos)
Else
;DebugLog(" ERROR! Search text does not match extracted string.")
Return "0"
End If
End If
End Function