1
0
Fork 0

Merge branch 'master' of gitorious.org:fg/fgdata into work

This commit is contained in:
Vivian Meazza 2011-10-07 13:01:17 +01:00
commit bee778ac2b
7 changed files with 280 additions and 76 deletions

View file

@ -43,8 +43,6 @@ The cloud is defined by the following properties:
<height-map-texture> - whether to choose the vertical texture index based on
sprite height within the clouds (default false)
<num-sprites> - Number of sprite to generate for the cloud (default 20)
<bottom-shade> - Light multiplier for sprites at the bottom of the cloud
(default 1.0)
<min-sprite-width-m> - minimum width of the sprites used to create the cloud
(default 200)
<max-sprite-width-m> - maximum width of the sprites used to create the cloud
@ -56,7 +54,39 @@ The cloud is defined by the following properties:
<z-scale> - vertical scaling factor to apply to to the sprite after
billboarding. A small value would create a sprite that
looks squashed when viewed from the side. (default 1.0)
<min-bottom-lighting-factor> - See Shading below (default 1.0)
<max-bottom-lighting-factor> - See Shading below (default min-...-factor + 0.1)
<min-middle-lighting-factor> - See Shading below (default 1.0)
<max-middle-lighting-factor> - See Shading below (default min-...-factor + 0.1)
<min-top-lighting-factor> - See Shading below (default 1.0)
<max-top-lighting-factor> - See Shading below (default min-...-factor + 0.1)
<min-shade-lighting-factor> - See Shading below (default 0.5)
<max-shade-lighting-factor> - See Shading below (default min-...-factor + 0.1)
Shading
-------
the [min|max]-...-lighting-factor properties allow you to define diffuse lighting
multipliers to the bottom, middle, top, sunny and shaded parts of the cloud. In
each case, individual clouds will have a random multiplier between the min and
max values used to allow for some variation between individual clouds.
The top, middle and bottom lighting factors are applied based on the pixels vertical
positon in the cloud. A linear interpolation is used either between top/middle (if
the pixel is above the middle of the cloud) or middle/bottom (if the pixel is below
the middle of the cloud).
The top factor is also applied to _all_ pixels on the sunny side of the cloud. The
shade factor is applied based on the pixel position away from the sun, linearly
interpolated from top to shade. E.g this is not a straight linear interpolation
from top to shade across the entire cloud.
The final lighting factor is determined by the minimum of the vertical factor and
the sunny/shade factor. Note that this is applied to the individual pixels, not
sprites.
Textures
--------
The texture to use for the sprites is defined in the <texture> tag.
To allow some variation, you can create a texture file containing multiple

View file

@ -60,6 +60,10 @@
<name>usrAttr2</name>
<index>11</index>
</attribute>
<attribute>
<name>usrAttr3</name>
<index>12</index>
</attribute>
</program>
<uniform>
<name>baseTexture</name>

View file

@ -35,33 +35,29 @@ ns = Nimbostratus (Rain cloud)
<clouds>
<cb-large>
<min-cloud-width-m>1600</min-cloud-width-m>
<max-cloud-width-m>2000</max-cloud-width-m>
<min-cloud-height-m>4000</min-cloud-height-m>
<max-cloud-height-m>3000</max-cloud-height-m>
<num-sprites>10</num-sprites>
<texture>cl_cumulus.png</texture>
<num-textures-x>4</num-textures-x>
<num-textures-y>4</num-textures-y>
<bottom-shade>0.3</bottom-shade>
<min-bottom-lighting-factor>0.3</min-bottom-lighting-factor>
<min-shade-lighting-factor>0.4</min-shade-lighting-factor>
<min-sprite-width-m>800</min-sprite-width-m>
<max-sprite-width-m>1200</max-sprite-width-m>
<min-sprite-height-m>800</min-sprite-height-m>
<max-sprite-height-m>1200</max-sprite-height-m>
<height-map-texture>true</height-map-texture>
</cb-large>
<cb-small>
<min-cloud-width-m>800</min-cloud-width-m>
<max-cloud-width-m>400</max-cloud-width-m>
<min-cloud-height-m>500</min-cloud-height-m>
<max-cloud-height-m>200</max-cloud-height-m>
<min-cloud-height-m>800</min-cloud-height-m>
<num-sprites>10</num-sprites>
<texture>cl_cumulus.png</texture>
<num-textures-x>4</num-textures-x>
<num-textures-y>4</num-textures-y>
<bottom-shade>0.3</bottom-shade>
<min-bottom-lighting-factor>0.3</min-bottom-lighting-factor>
<min-shade-lighting-factor>0.4</min-shade-lighting-factor>
<min-sprite-width-m>400</min-sprite-width-m>
<max-sprite-width-m>800</max-sprite-width-m>
<min-sprite-height-m>400</min-sprite-height-m>
<max-sprite-height-m>800</max-sprite-height-m>
<height-map-texture>true</height-map-texture>
</cb-small>
<cu-large>
<min-cloud-width-m>600</min-cloud-width-m>
@ -69,89 +65,95 @@ ns = Nimbostratus (Rain cloud)
<min-cloud-height-m>400</min-cloud-height-m>
<max-cloud-height-m>800</max-cloud-height-m>
<num-sprites>20</num-sprites>
<texture>cl_cumulus.png</texture>
<texture>cl_cumulus2.png</texture>
<num-textures-x>4</num-textures-x>
<num-textures-y>4</num-textures-y>
<bottom-shade>0.6</bottom-shade>
<min-bottom-lighting-factor>0.2</min-bottom-lighting-factor>
<min-shade-lighting-factor>0.3</min-shade-lighting-factor>
<min-sprite-width-m>400</min-sprite-width-m>
<max-sprite-width-m>700</max-sprite-width-m>
<min-sprite-height-m>300</min-sprite-height-m>
<max-sprite-height-m>700</max-sprite-height-m>
<height-map-texture>true</height-map-texture>
</cu-large>
<cu-small>
<min-cloud-width-m>300</min-cloud-width-m>
<max-cloud-width-m>800</max-cloud-width-m>
<min-cloud-height-m>200</min-cloud-height-m>
<max-cloud-height-m>300</max-cloud-height-m>
<num-sprites>10</num-sprites>
<texture>cl_cumulus.png</texture>
<min-cloud-width-m>600</min-cloud-width-m>
<max-cloud-width-m>900</max-cloud-width-m>
<min-cloud-height-m>400</min-cloud-height-m>
<max-cloud-height-m>600</max-cloud-height-m>
<num-sprites>20</num-sprites>
<texture>cl_cumulus2.png</texture>
<num-textures-x>4</num-textures-x>
<num-textures-y>4</num-textures-y>
<bottom-shade>0.7</bottom-shade>
<min-sprite-width-m>200</min-sprite-width-m>
<min-bottom-lighting-factor>0.4</min-bottom-lighting-factor>
<min-shade-lighting-factor>0.5</min-shade-lighting-factor>
<min-sprite-width-m>300</min-sprite-width-m>
<max-sprite-width-m>400</max-sprite-width-m>
<min-sprite-height-m>150</min-sprite-height-m>
<min-sprite-height-m>200</min-sprite-height-m>
<max-sprite-height-m>300</max-sprite-height-m>
<height-map-texture>true</height-map-texture>
</cu-small>
<ns-large>
<min-cloud-width-m>1200</min-cloud-width-m>
<max-cloud-width-m>3000</max-cloud-width-m>
<min-cloud-height-m>200</min-cloud-height-m>
<max-cloud-height-m>400</max-cloud-height-m>
<min-cloud-height-m>400</min-cloud-height-m>
<max-cloud-height-m>800</max-cloud-height-m>
<num-sprites>40</num-sprites>
<texture>cl_st.png</texture>
<num-textures-x>1</num-textures-x>
<num-textures-y>1</num-textures-y>
<bottom-shade>0.2</bottom-shade>
<min-bottom-lighting-factor>0.2</min-bottom-lighting-factor>
<min-sprite-width-m>300</min-sprite-width-m>
<max-sprite-width-m>600</max-sprite-width-m>
<min-sprite-height-m>100</min-sprite-height-m>
<max-sprite-height-m>200</max-sprite-height-m>
<height-map-texture>false</height-map-texture>
<z-scale>0.5</z-scale>
</ns-large>
<ns-white>
<min-cloud-width-m>600</min-cloud-width-m>
<max-cloud-width-m>2000</max-cloud-width-m>
<min-cloud-height-m>200</min-cloud-height-m>
<max-cloud-height-m>300</max-cloud-height-m>
<min-cloud-height-m>400</min-cloud-height-m>
<max-cloud-height-m>600</max-cloud-height-m>
<num-sprites>40</num-sprites>
<texture>cl_st.png</texture>
<num-textures-x>1</num-textures-x>
<num-textures-y>1</num-textures-y>
<bottom-shade>0.3</bottom-shade>
<min-bottom-lighting-factor>0.3</min-bottom-lighting-factor>
<min-sprite-width-m>300</min-sprite-width-m>
<max-sprite-width-m>600</max-sprite-width-m>
<min-sprite-height-m>100</min-sprite-height-m>
<max-sprite-height-m>200</max-sprite-height-m>
<height-map-texture>false</height-map-texture>
<z-scale>0.5</z-scale>
</ns-white>
<st-small>
<min-cloud-width-m>800</min-cloud-width-m>
<max-cloud-width-m>1500</max-cloud-width-m>
<min-cloud-height-m>100</min-cloud-height-m>
<max-cloud-height-m>200</max-cloud-height-m>
<num-sprites>40</num-sprites>
<texture>cl_st.png</texture>
<num-textures-x>1</num-textures-x>
<num-textures-y>1</num-textures-y>
<bottom-shade>0.9</bottom-shade>
<min-sprite-width-m>300</min-sprite-width-m>
<max-sprite-width-m>600</max-sprite-width-m>
<min-sprite-height-m>200</min-sprite-height-m>
<max-sprite-height-m>400</max-sprite-height-m>
</st-small>
<st-large>
<min-cloud-width-m>1800</min-cloud-width-m>
<max-cloud-width-m>2500</max-cloud-width-m>
<min-cloud-width-m>1000</min-cloud-width-m>
<min-cloud-height-m>200</min-cloud-height-m>
<max-cloud-height-m>400</max-cloud-height-m>
<num-sprites>40</num-sprites>
<texture>cl_st.png</texture>
<num-textures-x>1</num-textures-x>
<num-textures-y>1</num-textures-y>
<bottom-shade>0.9</bottom-shade>
<texture>cl_cumulus.png</texture>
<num-textures-x>4</num-textures-x>
<num-textures-y>4</num-textures-y>
<min-bottom-lighting-factor>0.9</min-bottom-lighting-factor>
<min-sprite-width-m>400</min-sprite-width-m>
<max-sprite-width-m>800</max-sprite-width-m>
<min-sprite-height-m>300</min-sprite-height-m>
<max-sprite-height-m>500</max-sprite-height-m>
<min-sprite-height-m>400</min-sprite-height-m>
<max-sprite-height-m>800</max-sprite-height-m>
<height-map-texture>false</height-map-texture>
<z-scale>0.3</z-scale>
</st-small>
<st-large>
<min-cloud-width-m>2000</min-cloud-width-m>
<min-cloud-height-m>1000</min-cloud-height-m>
<num-sprites>40</num-sprites>
<texture>cl_st.png</texture>
<num-textures-x>1</num-textures-x>
<num-textures-y>1</num-textures-y>
<min-bottom-lighting-factor>0.9</min-bottom-lighting-factor>
<min-sprite-width-m>600</min-sprite-width-m>
<min-sprite-height-m>600</min-sprite-height-m>
<height-map-texture>false</height-map-texture>
<z-scale>0.4</z-scale>
</st-large>
</clouds>
<boxes>

View file

@ -7,6 +7,65 @@
##
###############################################################################
###############################################################################
# Event broadcast channel using a MP enabled string property.
# Events from users in multiplayer.ignore are ignored.
#
# EventChannel.new(mpp_path)
# Create a new event broadcast channel. Any MP user with the same
# primitive will receive all messages sent to the channel from the point
# she/he joined (barring severe MP packet loss).
# NOTE: Message delivery is not guaranteed.
# mpp_path - MP property path : string
#
# EventChannel.register(event_hash, handler)
# Register a handler for the event identified by the hash event_hash.
# event_hash - hash value for the event : a unique 4 character string
# handler - a handler function for the event : func (msg)
#
# EventChannel.deregister(event_hash)
# Deregister the handler for the event identified by the hash event_hash.
# event_hash - hash value for the event : a unique 4 character string
#
# EventChannel.send(event_hash, msg)
# Sends the event event_hash with the message msg to the channel.
# event_hash - hash value for the event : a unique 4 character string
# msg - text string with Binary data encoded data : string
#
# EventChannel.die()
# Destroy this EventChannel instance.
#
var EventChannel = {};
EventChannel.new = func (mpp_path) {
var obj = BroadcastChannel.new(mpp_path,
func (n, msg) { obj._process(n, msg) });
# Save send from being overriden.
obj.parent_send = obj.send;
# Put EventChannel methods before BroadcastChannel methods.
obj.parents = [EventChannel] ~ obj.parents;
obj.events = {};
return obj;
}
EventChannel.register = func (event_hash,
handler) {
me.events[event_hash] = handler;
}
EventChannel.deregister = func (event_hash) {
delete(me.events, event_hash);
}
EventChannel.send = func (event_hash,
msg) {
me.parent_send(event_hash ~ msg);
}
############################################################
# Internals.
EventChannel._process = func (n, msg) {
var event_hash = Binary.readHash(msg);
if (contains(me.events, event_hash)) {
me.events[event_hash](substr(msg, Binary.sizeOf["Hash"]));
}
}
###############################################################################
# Broadcast primitive using a MP enabled string property.
# Broadcasts from users in multiplayer.ignore are ignored.
@ -60,6 +119,7 @@ BroadcastChannel.new = func (mpp_path, process,
send_buf : [],
peers : {},
loopid : 0,
running : 0,
PERIOD : 1.3,
last_time : 0.0, # For join handling.
last_send : 0.0, # For the send queue
@ -74,7 +134,8 @@ BroadcastChannel.new = func (mpp_path, process,
return obj;
}
BroadcastChannel.send = func (msg) {
if (me.send_node == nil) return;
if (!me.running or me.send_node == nil)
return;
var t = getprop("/sim/time/elapsed-sec");
if (((t - me.last_send) > me.SEND_TIME) and (size(me.send_buf) == 0)) {
@ -87,13 +148,21 @@ BroadcastChannel.send = func (msg) {
}
BroadcastChannel.die = func {
me.loopid += 1;
me.running = 0;
# print("BroadcastChannel[" ~ me.mpp_path ~ "] ... destroyed.");
}
BroadcastChannel.start = func {
me.loopid += 1;
settimer(func { me._loop_(me.loopid); }, 0, 1);
if (!getprop("/sim/multiplay/online")) {
me.stop();
} else {
#print("mp_broadcast.nas: starting channel " ~ me.send_node.getPath() ~ ".");
me.running = 1;
me._loop_(me.loopid += 1);
}
}
BroadcastChannel.stop = func {
#print("mp_broadcast.nas: stopping channel " ~ me.send_node.getPath() ~ ".");
me.running = 0;
me.loopid += 1;
}
@ -148,7 +217,10 @@ BroadcastChannel.update = func {
}
}
BroadcastChannel._loop_ = func (id) {
me.running or return;
id == me.loopid or return;
#print("mp_broadcast.nas: " ~ me.send_node.getPath() ~ ":" ~ id ~ ".");
me.update();
settimer(func { me._loop_(id); }, 0, 1);
}
@ -159,6 +231,7 @@ BroadcastChannel._loop_ = func(id) {
# NOTE: MP is picky about what it sends in a string propery.
# Encode 7 bits as a printable 8 bit character.
var Binary = {};
Binary.TWOTO28 = 268435456;
Binary.TWOTO31 = 2147483648;
Binary.TWOTO32 = 4294967296;
Binary.sizeOf = {};
@ -230,6 +303,21 @@ Binary.decodeCoord = func (str) {
Binary.decodeDouble(substr(str, 20)));
return coord;
}
############################################################
# Encodes a string as a hash value.
Binary.sizeOf["Hash"] = 4;
Binary.stringHash = func (str) {
var hash = 0;
for(var i=0; i<size(str); i+=1) {
hash += math.mod(32*hash + str[i], Binary.TWOTO28-3);
}
return substr(Binary.encodeInt(hash), 1, 4);
}
############################################################
# Decodes an encoded geo.Coord object.
Binary.readHash = func (str) {
return substr(str, 0, Binary.sizeOf["Hash"]);
}
######################################################################
###############################################################################

62
Nasal/scenery.nas Normal file
View file

@ -0,0 +1,62 @@
###############################################################################
##
## Support tools for multiplayer enabled scenery objects.
##
## Copyright (C) 2011 Anders Gidenstam (anders(at)gidenstam.org)
## This file is licensed under the GPL license version 2 or later.
##
###############################################################################
# The event channel for scenery objects.
# See mp_broadcast.EventChannel for documentation.
var events = nil;
###############################################################################
# An extended aircraft.door that transmits the door events over MP using the
# scenery.events channel.
# Use only for single instance objects (e.g. static scenery objects).
#
# Note: Currently toggle() is the only shared event.
var sharedDoor = {
new: func(node, swingtime, pos = 0) {
var obj = aircraft.door.new(node, swingtime, pos);
obj.parents = [sharedDoor] ~ obj.parents;
obj.event_hash = mp_broadcast.Binary.stringHash
(isa(node, props.Node) ? node.getPath() : node);
events.register(obj.event_hash, func (msg) { obj._process(msg) });
return obj;
},
toggle: func {
events.send(me.event_hash, mp_broadcast.Binary.encodeByte(me.target));
me.move(me.target);
},
destroy : func {
events.deregister(me.event_hash);
},
_process : func (msg) {
me.target = mp_broadcast.Binary.decodeByte(msg);
me.move(me.target);
}
};
###############################################################################
# Internals
var shared_pp = "scenery/share-events";
_setlistener("sim/signals/nasal-dir-initialized", func {
events = mp_broadcast.EventChannel.new("scenery/events");
if (!getprop("/sim/multiplay/online") or !getprop(shared_pp)) {
#print("scenery.nas: stopping event sharing.");
events.stop();
}
setlistener(shared_pp, func (n) {
if (getprop("/sim/signals/reinit")) return; # Ignore resets.
if (n.getValue() and getprop("/sim/multiplay/online")) {
#print("scenery.nas: starting event sharing.");
events.start();
} else {
#print("scenery.nas: stopping event sharing.");
events.stop();
}
});
});

View file

@ -7,13 +7,17 @@ uniform float range; // From /sim/rendering/clouds3d-vis-range
attribute vec3 usrAttr1;
attribute vec3 usrAttr2;
attribute vec3 usrAttr3;
float textureIndexX = usrAttr1.r;
float textureIndexY = usrAttr1.g;
float wScale = usrAttr1.b;
float hScale = usrAttr2.r;
float shade = usrAttr2.g;
float shade_factor = usrAttr2.g;
float cloud_height = usrAttr2.b;
float bottom_factor = usrAttr3.r;
float middle_factor = usrAttr3.g;
float top_factor = usrAttr3.b;
void main(void)
{
@ -34,11 +38,12 @@ void main(void)
gl_Position.xyz = gl_Vertex.x * u;
gl_Position.xyz += gl_Vertex.y * r * wScale;
gl_Position.xyz += gl_Vertex.z * w * hScale;
gl_Position.xyz += gl_Color.xyz;
// Apply Z scaling to allow sprites to be squashed in the z-axis
gl_Position.z = gl_Position.z * gl_Color.w;
// Now shift the sprite to the correct position in the cloud.
gl_Position.xyz += gl_Color.xyz;
// Determine a lighting normal based on the vertex position from the
// center of the cloud, so that sprite on the opposite side of the cloud to the sun are darker.
float n = dot(normalize(-gl_LightSource[0].position.xyz),
@ -48,21 +53,29 @@ void main(void)
vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Position);
float fogCoord = abs(ecPosition.z);
// Determine fractional height of vertex from 0 at the bottom, and 1 at mid-height.
// Used to determine shading.
float fract = smoothstep(0.0, cloud_height, gl_Position.z + cloud_height);
// Determine the shading of the vertex. We shade it based on it's position
// in the cloud relative to the sun, and it's vertical position in the cloud.
float shade = mix(shade_factor, top_factor, smoothstep(-0.3, 0.0, n));
//if (n < 0) {
// shade = mix(top_factor, shade_factor, abs(n));
//}
float h = gl_Position.z;
shade = min(shade,
min(mix(bottom_factor, middle_factor, smoothstep(0.0, 0.5 * h, h)),
mix(middle_factor, top_factor, smoothstep(0.5 * h, h, h)) ) );
//float h = gl_Position.z / cloud_height;
//if (h < 0.5) {
// shade = min(shade, mix(bottom_factor, middle_factor, smoothstep(0.0, 0.5, h)));
//} else {
// shade = min(shade, mix(middle_factor, top_factor, smoothstep(2.0 * (h - 0.5)));
// }
// Final position of the sprite
gl_Position = gl_ModelViewProjectionMatrix * gl_Position;
// Determine the shading of the sprite based on its vertical position and position relative to the sun.
n = min(smoothstep(-0.5, 0.0, n), fract);
// Determine the shading based on a mixture from the backlight to the front
vec4 backlight = gl_LightSource[0].diffuse * shade;
gl_FrontColor = mix(backlight, gl_LightSource[0].diffuse, n);
gl_FrontColor += gl_FrontLightModelProduct.sceneColor;
gl_FrontColor = gl_LightSource[0].diffuse * shade + gl_FrontLightModelProduct.sceneColor;
// As we get within 100m of the sprite, it is faded out. Equally at large distances it also fades out.
gl_FrontColor.a = min(smoothstep(10.0, 100.0, fogCoord), 1.0 - smoothstep(range*0.8, range, fogCoord));

View file

@ -1273,6 +1273,11 @@ Started September 2000 by David Megginson, david@megginson.com
</jetways_edit>
</nasal>
<scenery>
<share-events type="bool" userarchive="y">false</share-events>
<events type="string"/>
</scenery>
</PropertyList>
<!-- end of preferences.xml -->