In AI, are there any simple and/or very visual examples of how one could implement a genome into a simulation?
Basically, I'm after a simple walkthrough (not a tutorial, but rather something of a summarizing nature) which details how to implement a genome which changes the characteristics in an 'individual' in a sumlation.
These genes would not be things like:
Mass
Strength
Length,
Etc..
But rather they should be the things defining the above things, abstracting the genome from the actual characteristics of the inhabitants of the simulation.
Am I clear enough on what I want?
Anyway, if there's any way that you have tried that's better, and that implements evolution in a form like these sexual swimmers, then by all means, go ahead and post it! The more fun inspiration the better :)
If you are implementing your 'individuals' yourself then any object can act as your genome.
Characteristics
One way to simplify this further is to turn your characteristics into enums. This way you can have a simple recombination of the parent's genes by selecting characteristics from them and a mutation of the gene by random selection of one of the enum values for a characteristic.
Once this is working you can get more nuanced with value ranges but using enums helps me keeps things clear at first.
Fitness
Then to give these characteristics meaning you need a fitness function that describes performance. The relationship between the characteristics is up to you so you can describe it in any way that makes sense. This just provides a consistant way to compare two genomes.
Simulation
Then to run a simulation just start with a few parents and generate a bunch of children to complete with each other. This can of course be automated but here is an explicit example for clarity.
Java Example
import java.util.PriorityQueue;
class Genome implements Comparable<Genome> {
public enum Mass {
LIGHT(1),
AVERAGE(2),
HEAVY(3);
final Integer value;
Mass(Integer value) {
this.value = value;
}
}
public enum Strength {
WEAK(1),
AVERAGE(2),
STRONG(3);
final Integer value;
Strength(Integer value) {
this.value = value;
}
}
public enum Length {
SHORT(1),
AVERAGE(2),
LONG(3);
final Integer value;
Length(Integer value) {
this.value = value;
}
}
private final Mass mass;
private final Strength strength;
private final Length length;
public Genome(Mass mass, Strength strength, Length length) {
this.mass = mass;
this.strength = strength;
this.length = length;
}
private Integer fitness() {
return strength.value * length.value - mass.value * mass.value;
}
#Override public int compareTo(Genome that) {
// notice the fitter is less in precedence
if(this.fitness() > that.fitness())
return -1;
else if(this.fitness() < that.fitness())
return 1;
else // this.fitness() == that.fitness()
return 0;
}
public static Genome recombine(Genome... parents) {
if(parents.length < 1)
return null;
// Select parents randomly and then characteristics from them
Mass mass = parents[(int)(Math.random() * parents.length)].mass;
Strength strength = parents[(int)(Math.random() * parents.length)].strength;
Length length = parents[(int)(Math.random() * parents.length)].length;;
return new Genome(mass, strength, length);
}
public static Genome mutate(Genome parent) {
// Select characteristics randomly
Mass mass = Mass.values()[(int)(Math.random() * Mass.values().length)];
Strength strength = Strength.values()[(int)(Math.random() * Strength.values().length)];
Length length = Length.values()[(int)(Math.random() * Length.values().length)];
return new Genome(mass, strength, length);
}
public static void main() {
PriorityQueue<Genome> population = new PriorityQueue<Genome>();
Genome parent1 = new Genome(Mass.LIGHT, Strength.STRONG, Length.SHORT);
Genome parent2 = new Genome(Mass.AVERAGE, Strength.AVERAGE, Length.AVERAGE);
Genome parent3 = new Genome(Mass.HEAVY, Strength.WEAK, Length.LONG);
population.add(parent1);
population.add(parent2);
population.add(parent3);
Genome child1 = Genome.recombine(parent1, parent2);
Genome child2 = Genome.recombine(parent1, parent2);
Genome child3 = Genome.recombine(parent1, parent3);
Genome child4 = Genome.recombine(parent1, parent3);
Genome child5 = Genome.recombine(parent2, parent3);
Genome child6 = Genome.recombine(parent2, parent3);
Genome child7 = Genome.recombine(parent1, parent2, parent3);
Genome child8 = Genome.recombine(parent1, parent2, parent3);
Genome child9 = Genome.recombine(parent1, parent2, parent3);
child1 = Genome.mutate(child1);
child2 = Genome.mutate(child2);
child4 = Genome.mutate(child4);
child8 = Genome.mutate(child8);
population.add(child1);
population.add(child2);
population.add(child3);
population.add(child4);
population.add(child5);
population.add(child6);
population.add(child7);
population.add(child8);
population.add(child9);
// and the winner is...
Genome fittest = population.peek();
}
}
Encoding
Since it sounds like you want to encode the characteristics into a sequence having some characteristics explicit in the sequence and others derived from those.
You can do this my encoding your rangle of values, like the enums above, into an integer with chunks representing your explicit characteristics.
For example if you have two explicit characteristics with four possible values each you may encode the set as an integer in the form of 00XX + XX00. So for example 0111 might corresponds to a mass of 01 and a length of 11. What this does is let you mutate by changing the bits within the sequence itself.
Java Example
import java.util.PriorityQueue;
class Genome implements Comparable<Genome> {
private final Integer sequence;
private static final Integer bitmaskChunk = 3; // ...0011
private static final Integer shiftMass = 0; // ...00XX
private static final Integer shiftLength = 2; // ...XX00
private static final Integer shiftModulus = 4; // ...0000
private Integer getMass() {
return (sequence >>> shiftMass) & bitmaskChunk;
}
private Integer getLength() {
return (sequence >>> shiftLength) & bitmaskChunk;
}
public Integer getStrength() {
return getMass() * getLength();
}
public Genome(Integer sequence) {
this.sequence = sequence % (1 << Genome.shiftModulus);
}
private Integer fitness() {
// Some performance measure
return getStrength() * getLength() - getMass() * getMass();
}
#Override public int compareTo(Genome that) {
// notice the fitter is less in precedence
if(this.fitness() > that.fitness())
return -1;
else if(this.fitness() < that.fitness())
return 1;
else // this.fitness() == that.fitness()
return 0;
}
public static Genome recombine(Genome... parents) {
if(parents.length < 1)
return null;
Integer sequence = 0;
// Select parents randomly and then characteristics from them
sequence += parents[(int)(Math.random() * parents.length)].getMass() << Genome.shiftMass;
sequence += parents[(int)(Math.random() * parents.length)].getLength() << Genome.shiftLength;
return new Genome(sequence);
}
public static Genome mutate(Genome parent) {
Integer sequence = parent.sequence;
// Randomly change sequence in some way
sequence *= (int)(Math.random() * (1 << Genome.shiftModulus));
return new Genome(sequence);
}
public static void main() {
PriorityQueue<Genome> population = new PriorityQueue<Genome>();
Genome parent1 = new Genome((int)(Math.random() * (1 << Genome.shiftModulus)));
Genome parent2 = new Genome((int)(Math.random() * (1 << Genome.shiftModulus)));
Genome parent3 = new Genome((int)(Math.random() * (1 << Genome.shiftModulus)));
population.add(parent1);
population.add(parent2);
population.add(parent3);
Genome child1 = Genome.recombine(parent1, parent2);
Genome child2 = Genome.recombine(parent1, parent2);
Genome child3 = Genome.recombine(parent1, parent3);
Genome child4 = Genome.recombine(parent1, parent3);
Genome child5 = Genome.recombine(parent2, parent3);
Genome child6 = Genome.recombine(parent2, parent3);
Genome child7 = Genome.recombine(parent1, parent2, parent3);
Genome child8 = Genome.recombine(parent1, parent2, parent3);
Genome child9 = Genome.recombine(parent1, parent2, parent3);
child1 = Genome.mutate(child1);
child2 = Genome.mutate(child2);
child4 = Genome.mutate(child4);
child8 = Genome.mutate(child8);
population.add(child1);
population.add(child2);
population.add(child3);
population.add(child4);
population.add(child5);
population.add(child6);
population.add(child7);
population.add(child8);
population.add(child9);
// and the winner is...
Genome fittest = population.peek();
}
}
I hope this is what you are looking for. Good luck.
Related
So I am creating a card game that requires different cards, so I created a card class in which I declared the string value names and other integer values that are the powers eg. Intelligence
public static class hero{
static String name;
static int strength;
static int intellect;
static int flight;
static int tech;
}
So I created an array of instances of these classes.
Their names are read from a text file and assigned to the name value.
Q1) I am having trouble with reading through the file and assigning the string to the name value of each instance of the class.
This is what I've done so far
public static void readLines(File f)throws IOException{
FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
br.close();
fr.close();
}
static File f = new File("C:/Users/jeff/Desktop/test/names.txt");
try{
readLines(f);
} catch (IOException e){
e.printStackTrace();
}
Q2)The part I am also having trouble with is the part where I need to create a loop to randomly assign values to each power of each instance of a class.
Here's what I've done so far
{
hero [] cards = new hero[cardNumber];
for(int i=4;i<cardNumber;i++){ cards[i]=new hero();}
Random rand = new Random();
for(int i=0; i<cards.length; ++i)
{
cards[i].strength = rand.nextInt(25) + 1;
cards[i].intellect = rand.nextInt(25) + 1;
cards[i].flight = rand.nextInt(25) + 1;
cards[i].tech = rand.nextInt(25) + 1;
}
But when I print out the values all the instances have the same value for their powers.
Eg Card 12 Intelligence = 6
And Card 14 Intelligence = 6
Can anyone please help me with these issues, and any guidance will be highly appreciated
Thank you
I'm benchmarking Ignite and running the following tests:
Get 5K keys out of 100K/1M (3K are matched) using
cache.getAll(keys)
Query out of 100K/1M using:
List keys = cache.query(new ScanQuery(
(k, p) -> p.getSalary() > 900000)
Cache.Entry::getKey).getAll();
I have tuned Durable Memory as suggested here and I'm using separate ssd for storagePath and walPath but the results are not so good:
case 1 using 100K - 4,761ms
case 1 using 1M - 4,979
case 2 using 100K - 250ms
case 2 using 1M - 2,207ms
my object size is 1k. This is it:
public class MyPerson implements Serializable
{
/** Resume text (create LUCENE-based TEXT index for this field). */
#QueryTextField
private String resume;
/** MyPerson ID (indexed). */
#QuerySqlField(index = true)
private Long id;
/** Organization ID (indexed). */
#QuerySqlField(index = true)
private Long orgId;
/** First name (not-indexed). */
#QuerySqlField
private String firstName;
/** Last name (not indexed). */
#QuerySqlField
private String lastName;
/** Salary (indexed). */
#QuerySqlField(index = true)
private double salary;
public void setResume(String resume) {
this.resume = resume;
}
public Long getId() {
return id;
}
public MyPerson(Long id){
this.id = id;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
How can I improve my results?
Thanks,
Alon
I'm writing a program but I'm unable to call a few of the methods I made. The errors are as follows:
-method reportMenu(String) in the type CommissionReportSystem is not applicable for the arguments ()
-Cannot make a static reference to the non-static method getSalesData() from the type CommissionReportSystem
-The method computeTotalSales() is undefined for the type CommissionReportSystem
-The method computeSalesCommission(double) in the type CommissionReportSystem is not applicable for the arguments ()
-The method showAgentCommission(double) in the type CommissionReportSystem is not applicable for the arguments ()
I've tried a lot of fixes but nothing seems to be sticking and I'm unsure of how to proceed. I've included the relevant parts of the code below. I would appreciate any tips on how to fix any of these. Thank you!
import java.io.*;
import java.text.*;
import java.util.*;
public class CommissionReportSystem {
private static final String String = null;
public static void main(String[] args) {
getSalesData ();
computeTotalSales ();
computeSalesCommission ();
showAgentCommission ();
shutdown ();
}
String [] getSalesData (){
String [] data = new String [2];
String ticketsSold = "";
String ticketPrice = "";
String buffer = new String ();
data[0] = buffer;
data[1] = buffer;
BufferedReader br = null;
try {
br = new BufferedReader (new InputStreamReader(System.in));
System.out.print ("Enter tickets sold:");
buffer = br.readLine ();
ticketsSold = buffer;
System.out.print ("Enter ticket price:");
buffer = br.readLine ();
ticketPrice = buffer;
} catch (Exception e) {
System.out.println ("Invalid entry");
}
data [0] = ticketsSold;
data [1] = ticketPrice;
return data;
}
public static double totalSales (String ticketsSold, String ticketPrice){
int ticketsSoldNum = Integer.parseInt(ticketsSold);
double ticketPriceNum = Double.parseDouble(ticketPrice);
double totalSalesNum = ticketsSoldNum * ticketPriceNum;
return totalSalesNum;}
public static final double computeSalesCommission (double totalSalesNum){
final double rate1 = 0.025;
final double rate2 = 0.0375;
final double rate3 = 0.0425;
final double salesLimit1 = 2000;
final double salesLimit2 = 4000;
final double agentCommission= 0;
if (totalSalesNum <= 2000) {
agentCommission = rate1 * totalSalesNum;
}
else if (totalSalesNum <= 4000){
agentCommission = rate2 * totalSalesNum;
}
else (totalSalesNum > 4000){
agentCommission = rate3 * totalSalesNum;
}
return agentCommission;
}
public static void showAgentCommission (double agentCommission){
System.out.format ("Congratulation agent Cindy Smith, your current daily commission:" + agentCommission);
}
public static void shutdown (){
System.out.format ("Thank you for your time! Have a great day!");
}
public static void handleInvalidData (){
}
}
1) getSalesData() is an instance method. If you want to call an instance method, create an object of the class and call method using that. Else you have to make the method static. Remember one the thing you cannot access the instance variables inside static method.
2) There is no method computeTotalSales() in your class.
3) computeSalesCommission() requires an argument of type double. You have called it without any argument.
4) The last comment is also valid for showAgentCommission().
I have WPF application with PcapDotNet DLL's that measure my machine Interface Rate.
This is the Model:
public class Interface
{
public PacketDevice PacketDevice { get { return livePacketDevice; } }
private DateTime _lastTimestamp;
private double _bitsPerSecond;
private double _packetsPerSecond;
private DateTime _lastTimestamp;
private static List<Interface> _machineInterfaces; // list of all machine interfaces
public void Start(Interface inf)
{
OpenAdapterForStatistics(inf.PacketDevice);
}
public void OpenAdapterForStatistics(PacketDevice selectedOutputDevice)
{
if (selectedOutputDevice != null)
{
using (PacketCommunicator statCommunicator = selectedOutputDevice.Open(100, PacketDeviceOpenAttributes.Promiscuous, 1000)) //open the output adapter
{
try
{
statCommunicator.Mode = PacketCommunicatorMode.Statistics; //put the interface in statstics mode
statCommunicator.ReceiveStatistics(0, StatisticsHandler); //start the main loop
}
catch (Exception)
{ }
}
}
}
private void StatisticsHandler(PacketSampleStatistics statistics)
{
DateTime currentTimestamp = statistics.Timestamp; //current sample time
DateTime previousTimestamp = _lastTimestamp; //previous sample time
_lastTimestamp = currentTimestamp; //set _lastTimestamp for the next iteration
if (previousTimestamp == DateTime.MinValue) //if there wasn't a previous sample than skip this iteration (it's the first iteration)
return;
double delayInSeconds = (currentTimestamp - previousTimestamp).TotalSeconds; //calculate the delay from the last sample
_bitsPerSecond = statistics.AcceptedBytes * 8 / delayInSeconds; //calculate bits per second
_packetsPerSecond = statistics.AcceptedPackets / delayInSeconds; //calculate packets per second
if (NewPointEventHandler != null)
NewPointEventHandler(_bitsPerSecond);
double value = _packetsPerSecond;
}
As you can see Start method start to measure the Interface rate and put the values into 2 fields:
_bitsPerSecond and _packetsPerSecond.
So after the application start i have this field:
List<Interface> _machineInterfaces;
That read all my machine interfaces.
After that i start my Start method:
private void StartStatistics()
{
int index = listview.SelectedIndex; // select the selected interface from my `ListView` list.
Interface inf = new Interface();
ThreadStart tStarter = delegate
{
inf.Start(Interface.MachineInterfaces[index]); // send the selected interface
};
Thread thread = new Thread(tStarter);
thread.IsBackground = true;
thread.Start();
statisticsTimer.Start(); // start my timer
}
OK so now this is my Question:
This is my Timer Tick Event:
public RadObservableCollection<double> mbitPerSecondValue { get; private set; }
If my BitsPerSecond Class Interface member define as regular and not Static it's value is always zero:
private void statisticsTimer_Tick(object sender, EventArgs e)
{
int index = listview.SelectedIndex;
double bps = Interface.MachineInterfaces[index].BitsPerSecond; // always zero !
mbitPerSecondValue.Add(bps);
}
In case BitsPerSecond define as static all good:
private void statisticsTimer_Tick(object sender, EventArgs e)
{
int index = listview.SelectedIndex;
double bps = Interface.BitsPerSecond;
mbitPerSecondValue.Add(bps);
}
So my question is why ?
Edit
Currently i changed my function:
private void StartStatistics()
{
int index = lvAdapters.SelectedIndex;
Interface inf = new Interface();
ThreadStart tStarter = delegate
{
foreach (Interface item in Interface.MachineInterfaces)
item.Start();
};
Thread thread = new Thread(tStarter);
thread.IsBackground = true;
thread.Start();
statisticsTimer.Start();
}
What i want to achieve is to open statistics on each interface on my machine , but again in the first interface (i have 2) i can see the traffic changing (BitsPerSecond) but in the second interface it's always zero (i make sure to generate some traffic through this interface so it not supposed to be zero)
For your second question, try to call each Interface's Start from different threads. The only suspicious thing I see is that maybe statCommunicator.ReceiveStatistics is blocking the thread and preventing the other Interfaces from being Started.
This should avoid that problem:
private void StartStatistics()
{
foreach (Interface item in Interface.MachineInterfaces)
{
ThreadStart tStarter = delegate
{
item.Start();
};
Thread thread = new Thread(tStarter);
thread.IsBackground = true;
thread.Start();
}
statisticsTimer.Start();
}
Well, it's obvious why it's working when defined as static: all instances of Interface are sharing the same property, so when you increment its value from one place, the new value is automatically available everywhere.
But as a regular non-static property, you have to make sure you're reading from the same instance you've previously modified. And you're not.
First of all, you're creating a new Interface (let's call it Interface A) and then calling its Start, passing another Interface (that we'll call Interface B) that you get from Interface.MachineInterfaces, as parameter:
private void StartStatistics()
{
...
Interface inf = new Interface();
ThreadStart tStarter = delegate
{
inf.Start(Interface.MachineInterfaces[index]); // send the selected interface
};
...
}
Inside the Start method of Interface A, you're subscribing to the statistics of Interface B, but the handler is still in Interface A:
public void Start(Interface inf)
{
OpenAdapterForStatistics(inf.PacketDevice);
}
public void OpenAdapterForStatistics(PacketDevice selectedOutputDevice)
{
...
statCommunicator.ReceiveStatistics(0, StatisticsHandler); //start the main loop
...
}
And when the handler in Interface A is called, it increments its own _bitsPerSecond value. Not Interface B's, but Interface A's.
private void StatisticsHandler(PacketSampleStatistics statistics)
{
...
_bitsPerSecond = statistics.AcceptedBytes * 8 / delayInSeconds; //calculate bits per second
...
}
But in the end, you're checking the value of BitsPerSecond in Interface B, taken again from Interface.MachineInterfaces!
private void statisticsTimer_Tick(object sender, EventArgs e)
{
...
double bps = Interface.MachineInterfaces[index].BitsPerSecond; // always zero !
...
}
-- SUGGESTED SOLUTION 1 --
Why don't you just make it so Start uses its own instance, so you don't have to create a new Interface just to use it?
public void Start()
{
OpenAdapterForStatistics(this.PacketDevice);
}
That way you can just do:
private void StartStatistics()
{
int index = listview.SelectedIndex; // select the selected interface from my `ListView` list.
ThreadStart tStarter = delegate
{
Interface.MachineInterfaces[index].Start(); // send the selected interface
};
Thread thread = new Thread(tStarter);
thread.IsBackground = true;
thread.Start();
statisticsTimer.Start(); // start my timer
}
...And you should get the desired output in your Timer Tick callback.
-- SUGGESTED SOLUTION 2 --
If you don't want to call Start from the original Interfaces inside Interface.MachineInterfaces, then you'll have to store the new Interface in some kind of Dictionary, so you can access it later to get BitsPerSecond from it:
private Dictionary<Interface, Interface> InterfaceDictionary = new Dictionary<Interface, Interface>();
private void StartStatistics()
{
int index = listview.SelectedIndex; // select the selected interface from my `ListView` list.
Interface inf = new Interface();
ThreadStart tStarter = delegate
{
inf.Start(Interface.MachineInterfaces[index]); // send the selected interface
};
Thread thread = new Thread(tStarter);
thread.IsBackground = true;
thread.Start();
statisticsTimer.Start(); // start my timer
if (InterfaceDictionary.ContainsKey(Interface.MachineInterfaces[index]))
InterfaceDictionary[Interface.MachineInterfaces[index]] = inf;
else
InterfaceDictionary.Add(Interface.MachineInterfaces[index], inf);
}
And in your Timer Tick callback, grab the data from the associated Interface, not from the one in Interface.MachineInterfaces:
private void statisticsTimer_Tick(object sender, EventArgs e)
{
int index = listview.SelectedIndex;
var interface = InterfaceDictionary[Interface.MachineInterfaces[index]];
double bps = interface.BitsPerSecond;
mbitPerSecondValue.Add(bps);
}
I have a 2D node scene graph that I'm trying to 'nest' stencil clipping in.
I was thinking what I could do is when drawing the stencil, increment any pixel it writes to by 1, and keep track of what the current 'layer' is that I'm on.
Then when drawing, only write pixel data to the color buffer if the value of the stencil at that pixel is >= the current layer #.
This is the code I have now. It doesn't quite work. Where am I messing up?
First I call SetupStencilForMask().
Then draw stencil primitives.
Next, call SetupStencilForDraw().
Now draw actual imagery
When done with a layer, call DisableStencil().
Edit: Updated with solution. It doesn't work for individual items on the same layer, but otherwise is fine.
Found a great article on how to actually pull this off, although it's fairly limited.
http://cranialburnout.blogspot.com/2014/03/nesting-and-overlapping-translucent.html
// glClear(GL_STENICL_BIT) at start of each draw frame
static int stencilLayer = 0;
void SetupStencilForMask(void)
{
if (stencilLayer == 0)
glEnable(GL_STENCIL_TEST);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glStencilFunc(GL_LESS, stencilLayer, 0xff);
glStencilOp(GL_INCR, GL_KEEP, GL_KEEP);
glStencilMask(0xff);
if (stencilLayer == 0)
glClear(GL_STENCIL_BUFFER_BIT);
stencilLayer++;
}
void SetupStencilForDraw()
{
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glStencilFunc(GL_EQUAL, stencilLayer, 0xff);
glStencilMask(0x00);
}
void DisableStencil(void)
{
if (--stencilLayer == 0)
glDisable(GL_STENCIL_TEST);
}
I have figured out the way to do this in libgdx. I'm not sure you still need this, but for future reference here is the code:
/**
* Start cropping
*
* #param cropMask
* Mask plane
*/
public void startCropping(Plane cropMask) {
// Check if there is active masking group
if (activeCropMaskGroup == null) {
// Create new one
activeCropMaskGroup = new CropMaskingGroupDescriptor(cropMask);
} else {
// Increase hierarchy level
activeCropMaskGroup.increaseHierachy(cropMask);
}
}
/** End cropping */
public void endCropping() throws IllegalStateException {
// Check if there is active group mask
if (activeCropMaskGroup == null) {
throw new IllegalStateException("Call start cropping before this!");
}
if (activeCropMaskGroup.getHierachy() > 0) {
activeCropMaskGroup.decreaseHierachy();
} else {
// Finish setup of crop data
cropMaskGroups.add(activeCropMaskGroup);
activeCropMaskGroup = null;
}
}
/** Crop registered planes for cropping */
private void cropRender(CropMaskingGroupDescriptor cropMaskGroupDescriptor) {
// Draw mask to stencil buffer
Gdx.gl.glClear(GL20.GL_STENCIL_BUFFER_BIT);
// setup drawing to stencil buffer
Gdx.gl20.glEnable(GL20.GL_STENCIL_TEST);
// Number of registered hierarchy levels
int hierarchyLevels = cropMaskGroupDescriptor.getRegisteredBatch().size();
// Loop trough hierarchy
for (int hierarchyLevel = 0; hierarchyLevel < hierarchyLevels; hierarchyLevel++) {
Gdx.gl20.glStencilFunc(GL20.GL_ALWAYS, 0x1, 0xffffffff);
Gdx.gl20.glStencilOp(GL20.GL_INCR, GL20.GL_INCR, GL20.GL_INCR);
Gdx.gl20.glColorMask(false, false, false, false);
Gdx.gl20.glDepthMask(false);
// Draw mask with decal batch
cropMaskBatch.add(((NativePlane) cropMaskGroupDescriptor.getCroppingMasks().get(hierarchyLevel)).getPlane());
cropMaskBatch.flush();
// fix stencil buffer, enable color buffer
Gdx.gl20.glColorMask(true, true, true, true);
Gdx.gl20.glDepthMask(true);
Gdx.gl20.glStencilOp(GL20.GL_KEEP, GL20.GL_KEEP, GL20.GL_KEEP);
// draw where pattern has been drawn
Gdx.gl20.glStencilFunc(GL20.GL_LEQUAL, hierarchyLevel + 1, 0xffffffff);
// Loop trough registered masked layers and found which one belongs
// to
// current hierarchy level
for (int i = 0; i < cropMaskGroupDescriptor.getMaskedLayers().size(); i++) {
if (cropMaskGroupDescriptor.getMaskedLayers().get(i).getHierarchyId() == hierarchyLevel) {
Plane plane = cropMaskGroupDescriptor.getMaskedLayers().get(i).getMaskedPlane();
cropMaskGroupDescriptor.getRegisteredBatch().get(hierarchyLevel).add(((NativePlane) plane).getPlane());
}
}
cropMaskGroupDescriptor.getRegisteredBatch().get(hierarchyLevel).flush();
}
Gdx.gl20.glDisable(GL20.GL_STENCIL_TEST);
}
And inner class inside renderer module.
/**
* Cropped layer descriptor
*
* #author Veljko Ilkic
*
*/
private class CropMaskLayerDescriptor {
/** Layer that needs to be masked */
private Plane maskedPlane;
/** Hierarchy level in which belongs */
private int hierarchyId = 0;
/** Constructor 1 */
public CropMaskLayerDescriptor(Plane maskedPlane, int hierarchyId) {
this.maskedPlane = maskedPlane;
this.hierarchyId = hierarchyId;
}
public Plane getMaskedPlane() {
return maskedPlane;
}
public int getHierarchyId() {
return hierarchyId;
}
}
/**
* Crop masking group descriptor class
*
* #author Veljko Ilkic
*
*/
private class CropMaskingGroupDescriptor {
/** Crop mask */
private ArrayList<Plane> croppingMasks = new ArrayList<Plane>();
/** Planes that will be masked by crop mask */
private ArrayList<CropMaskLayerDescriptor> maskedLayers = new ArrayList<Renderer.CropMaskLayerDescriptor>();
/** Batch for drawing masked planes */
private ArrayList<DecalBatch> hierarchyBatches = new ArrayList<DecalBatch>();
private int activeHierarchyLayerId = 0;
/** Constructor 1 */
public CropMaskingGroupDescriptor(Plane topLevelCropMask) {
// Create batch for top level hierarchy
hierarchyBatches.add(new DecalBatch(new CameraGroupStrategy(perspectiveCamera)));
// Register top level crop mask
croppingMasks.add(topLevelCropMask);
}
/** Increase hierarchy level of the group */
public void increaseHierachy(Plane hierarchyCropMask) {
activeHierarchyLayerId++;
// Create individual batch for hierarchy level
hierarchyBatches.add(new DecalBatch(new CameraGroupStrategy(perspectiveCamera)));
// Register crop mask for current hierarchy level
croppingMasks.add(hierarchyCropMask);
}
/** Decrease hierarchy group */
public void decreaseHierachy() {
activeHierarchyLayerId--;
}
/** Get current hierarchy level */
public int getHierachy() {
return activeHierarchyLayerId;
}
/** Register plane for masking */
public void registerLayer(Plane maskedPlane) {
hierarchyBatches.get(activeHierarchyLayerId).add(((NativePlane) maskedPlane).getPlane());
maskedLayers.add(new CropMaskLayerDescriptor(maskedPlane, activeHierarchyLayerId));
}
/** Get all registered batched */
public ArrayList<DecalBatch> getRegisteredBatch() {
return hierarchyBatches;
}
/** Get registered cropping masks */
public ArrayList<Plane> getCroppingMasks() {
return croppingMasks;
}
/** Get layer that should be masked */
public ArrayList<CropMaskLayerDescriptor> getMaskedLayers() {
return maskedLayers;
}
/** Dispose */
public void dispose() {
for (int i = 0; i < hierarchyBatches.size(); i++) {
hierarchyBatches.get(i).dispose();
hierarchyBatches.set(i, null);
}
hierarchyBatches.clear();
}
}
Hope this helps.