• What Are You Working On? September 2015
    1,261 replies, posted
[QUOTE=Tamschi;48659287][I]System.Reflection.Emit[/I] isn't necessarily a core API, so there are quite a few places that don't support it. (Most notably Unity, unless that recently changed.) If you don't compile the getters and setters the resulting query will run very slowly due to the amount of type checking and dynamic casts involved.[/QUOTE] As I suspected, the IEnumerable is causing problems. The issue is that I want to declare extension methods on `Setter<IEnumerable<T>>` and that I want it to work with things like `Setter<List<T>>`. No dice. This is one of the examples why subtyping presents issues as soon as you start including smarter type system features like extension methods. It can't demote the `Setter<List<T>>` to `Setter<IEnumerable<T>>` because then it would have to demote things like `Func<List<T>, U>` to `Func<IEnumerable<T>, U>` which breaks things.
So after the axle coupling spontaneously disintegrated (it was mostly held together by superglue :v:), I've mounted a proper axle mounting that arrived the other day: [t]https://d.maxfile.ro/qwccsmemfc.jpg[/t] I had to extend the outer diameter of the axis with ducttape, but I think it'll make a very stable connection.
It looks like a cool teleporter animation. [editline]11th September 2015[/editline] Maybe if it were more white blending into colors or something.
[QUOTE=ruarai;48659537][img]http://zippy.gfycat.com/FragrantSelfishAlpaca.gif[/img] photoshop is hard so i made this cool animation instead[/QUOTE] How do you do it? That's one cool as fuck effect
[QUOTE=Darwin226;48659440]As I suspected, the IEnumerable is causing problems. The issue is that I want to declare extension methods on `Setter<IEnumerable<T>>` and that I want it to work with things like `Setter<List<T>>`. No dice. This is one of the examples why subtyping presents issues as soon as you start including smarter type system features like extension methods. It can't demote the `Setter<List<T>>` to `Setter<IEnumerable<T>>` because then it would have to demote things like `Func<List<T>, U>` to `Func<IEnumerable<T>, U>` which breaks things.[/QUOTE] What exactly are you trying to do? For the normal methods you should just have [I]IEnumerable<Tuple<T, Setter<T>>>[/I] or something along those lines, if you follow the pattern of the normal [I]IEnumerable<>[/I] extensions. If you want to operate on more specific values you can always constrain them to e.g. [I]IList<>[/I] if you need to shuffle a collection around. If you need to create instances for some reason add a [I]new()[/I] constraint to the generic type parameter. [editline]11th September 2015[/editline] If you just want to read the value, you can use e.g. [I]IEnumerable<Tuple<TValue, TIgnored>>[/I], but normally you shouldn't need to have that if there's no collision between [I]Select[/I]([I]Many[/I]) and [I]SelectGetSet[/I].
That's not how it works. A Setter<T, U> is a setter that can modify some inner part of T of the type U. If I have a setter Setter<T, U> and do setter.Prop(obj => obj.SomeProperty) and SomeProperty is of type V then the resulting setter has the type Setter<T, V>. I can't make an IEnumerable<Setter<T, R>> in case V turns out to be a collection of Rs because the type is always a Setter<something>. I could try to hack that in but it's the same thing if I add special cases here or if I add special cases for various IEnumerables with my current setup. With the added benefit that I don't have a collection of setters that doesn't semantically make much sense.
I made some [I]Modify[/I]([I]All[/I]) extension methods with split type parameters, which will still work if any of them change along the way: [code]public static void Modify<TIn, TOut>(this IEnumerable<Tuple<TIn, Action<TOut>>> modifiables, Func<TIn, TOut> modifier) { foreach (var modifiable in modifiables) { modifiable.Item2(modifier(modifiable.Item1)); } } public static void Modify<TIn, TOut>(this IEnumerable<Tuple<TIn, Action<TOut>>> modifiables, Func<TIn, int, TOut> modifier) { int i = 0; foreach (var modifiable in modifiables) { modifiable.Item2(modifier(modifiable.Item1, i++)); } } public static void ModifyAll<TIn, TOut>(this IEnumerable<Tuple<TIn, Action<TOut>>> modifiables, Func<List<TIn>, IReadOnlyList<TOut>> modifier) { var inputs = new List<TIn>(); var outputs = new List<Action<TOut>>(); foreach (var modifiable in modifiables) { inputs.Add(modifiable.Item1); outputs.Add(modifiable.Item2); } var values = modifier(inputs); if (values.Count != outputs.Count) { throw new ArgumentException("The item count must not change.", nameof(modifier)); } for (int i = 0; i < values.Count; i++) { outputs[i](values[i]); } }[/code] Type inference works as normal with these: [code]IEnumerable<Tuple<bool, Action<bool>>> modifiables = null; modifiables.Modify(b => !b); modifiables.Modify((b, i) => i % 2 == 0 ? b : !b); modifiables.ModifyAll(bs => { bs.Reverse(); return bs; });[/code]
[QUOTE=Tamschi;48660451]I made some [I]Modify[/I]([I]All[/I]) extension methods with split type parameters, which will still work if any of them change along the way: [code]public static void Modify<TIn, TOut>(this IEnumerable<Tuple<TIn, Action<TOut>>> modifiables, Func<TIn, TOut> modifier) { foreach (var modifiable in modifiables) { modifiable.Item2(modifier(modifiable.Item1)); } } public static void Modify<TIn, TOut>(this IEnumerable<Tuple<TIn, Action<TOut>>> modifiables, Func<TIn, int, TOut> modifier) { int i = 0; foreach (var modifiable in modifiables) { modifiable.Item2(modifier(modifiable.Item1, i++)); } } public static void ModifyAll<TIn, TOut>(this IEnumerable<Tuple<TIn, Action<TOut>>> modifiables, Func<List<TIn>, IReadOnlyList<TOut>> modifier) { var inputs = new List<TIn>(); var outputs = new List<Action<TOut>>(); foreach (var modifiable in modifiables) { inputs.Add(modifiable.Item1); outputs.Add(modifiable.Item2); } var values = modifier(inputs); if (values.Count != outputs.Count) { throw new ArgumentException("The item count must not change.", nameof(modifier)); } for (int i = 0; i < values.Count; i++) { outputs[i](values[i]); } }[/code] Type inference works as normal with these: [code]IEnumerable<Tuple<bool, Action<bool>>> modifiables = null; modifiables.Modify(b => !b); modifiables.Modify((b, i) => i % 2 == 0 ? b : !b); modifiables.ModifyAll(bs => { bs.Reverse(); return bs; });[/code][/QUOTE] I don't think I understand what you wrote. Can you write an example like mine? Anyways, I just found this [url]https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx[/url] You can apparently make your own covariant and contravariant generic types. NICE!
[QUOTE=Darwin226;48660434]That's not how it works. A Setter<T, U> is a setter that can modify some inner part of T of the type U. If I have a setter Setter<T, U> and do setter.Prop(obj => obj.SomeProperty) and SomeProperty is of type V then the resulting setter has the type Setter<T, V>. I can't make an IEnumerable<Setter<T, R>> in case V turns out to be a collection of Rs because the type is always a Setter<something>. I could try to hack that in but it's the same thing if I add special cases here or if I add special cases for various IEnumerables with my current setup. With the added benefit that I don't have a collection of setters that doesn't semantically make much sense.[/QUOTE] I really don't get where the problem is here. If you want to modify inner collections separately that's [I].Select(...).SelectGetSet(...)[/I]. If you want to modify all of them at once then that's [I].SelectMany(...).SelectGetSet(...)[/I], where [I].Select(...)[/I] and [I].SelectMany(...)[/I] are default LINQ to objects.
Which only works for interfaces... Damn.
[QUOTE=Darwin226;48660485]I don't think I understand what you wrote. Can you write an example like mine? Anyways, I just found this [url]https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx[/url] You can apparently make your own covariant and contravariant generic types. NICE![/QUOTE] Yes, [I]IEnumerable<[B]out[/B] T>[/I] has that by default. It only works with interfaces though. [editline]11th September 2015[/editline] [QUOTE=Darwin226;48660485]I don't think I understand what you wrote. Can you write an example like mine? [...][/QUOTE] Give me a moment. I'll use default .NET types for the most part though.
[QUOTE=Tamschi;48660486]I really don't get where the problem is here. If you want to modify inner collections separately that's [I].Select(...).SelectGetSet(...)[/I]. If you want to modify all of them at once then that's [I].SelectMany(...).SelectGetSet(...)[/I], where [I].Select(...)[/I] and [I].SelectMany(...)[/I] are default LINQ to objects.[/QUOTE] I'd really need to see an example here.
[QUOTE=Darwin226;48660496]Which only works for interfaces... Damn.[/QUOTE] It's fine if you combine it with generics and constraints (but it is a bit clunky).
You can use these [code] class Game { public World World; } class World { public List<Player> Players; } class Player { public string Name; public Data Data; } class Data { public List<int> Pos; } static void Main(string[] args) { var o = new Game { World = new World { Players = new List<Player> { new Player { Name="Steve", Data = new Data { Pos = new List<int> { 0, 1 } } }, new Player { Name="Dave", Data = new Data { Pos = new List<int> { 1, 2 } } }, new Player { Name="Bob", Data = new Data { Pos = new List<int> { 2, 3 } } }, new Player { Name="Hank", Data = new Data { Pos = new List<int> { 3, 4 } } } } } }; }[/code] Jesus, the automerge is dead. Edit: I just realized that since the setters are bidirectional they can't be covariant or contravariant anyways. I need special cases for various collections. It doesn't make sense otherwise.
Looks like I'm going to add a cubemap pass next, as that got the most votes. I think I should be able to add a [I][B]default cubemap[/B][/I] without much difficulty; it would go something like this: Geometry pass: [t]http://i.imgur.com/FO4aSL9.png[/t] In fragment shader, convert uploaded surface reflectance values into a texture map: [t]http://i.imgur.com/N9ROq1n.png[/t] Perform a reflection pass, where the previous texture is sampled and, higher rgb values (probably just a single value) = reflect more. Also sample from the default cubemap (== skybox) based on the reflection angle or however it is that is done: [t]http://i.imgur.com/pzk9Rhf.png[/t] Then blend back into the scene. This should work fine for the default reflection map, however games can manually place custom cubemaps all over the map to add more detail to specific sources, so I think in that case I will perform an operation similar to my point lights, where I will make the cubemaps (outside of the default one) [I]volumentric[/I], and make use of stenciling to limit the cubemap to the outermost boundary of the sphere put in place to represent it.
[QUOTE=Darwin226;48660513]I'd really need to see an example here.[/QUOTE] LinqBack library (incomplete): [code]using System; namespace LinqBack { public struct ValueSetterPair<TValue, TSet> { public TValue Value { get; } public Action<TSet> Setter { get; } public ValueSetterPair(TValue value, Action<TSet> setter) { Value = value; Setter = setter; } } }[/code][code]using System; using System.Collections.Generic; using System.Linq.Expressions; namespace LinqBack { public static class LinqBackExtensions { public static IEnumerable<ValueSetterPair<TValue, TSet>> SelectValueSetterPair<TItem, TValue, TSet>(this IEnumerable<TItem> source, Func<TItem, TValue> projector, Func<TItem, Action<TSet>> setterSelector) { foreach (var item in source) { yield return new ValueSetterPair<TValue, TSet>(projector(item), setterSelector(item)); } } public static IEnumerable<ValueSetterPair<TModifiable, TModifiable>> SelectModifiable<TItem, TModifiable>(this IEnumerable<TItem> source, Expression<Func<TItem, TModifiable>> propertyGetter) { var get = propertyGetter.Compile(); var itemParam = Expression.Parameter(typeof(TItem)); var valueParam = Expression.Parameter(typeof(TModifiable)); var setter = Expression.Lambda<Action<TItem, TModifiable>>( Expression.Assign( Expression.MakeMemberAccess( itemParam, ((MemberExpression)propertyGetter.Body).Member), valueParam), itemParam, valueParam).Compile(); foreach (var item in source) { yield return new ValueSetterPair<TModifiable, TModifiable>(get(item), value => setter(item, value)); } } public static void Modify<TIn, TOut>(this IEnumerable<ValueSetterPair<TIn, TOut>> modifiables, Func<TIn, TOut> modifier) { foreach (var modifiable in modifiables) { modifiable.Setter(modifier(modifiable.Value)); } } public static void Modify<TIn, TOut>(this IEnumerable<ValueSetterPair<TIn, TOut>> modifiables, Func<TIn, int, TOut> modifier) { int i = 0; foreach (var modifiable in modifiables) { modifiable.Setter(modifier(modifiable.Value, i++)); } } public static void ModifyAll<TIn, TOut>(this IEnumerable<ValueSetterPair<TIn, TOut>> modifiables, Func<IList<TIn>, IList<TOut>> modifier) { var inputs = new List<TIn>(); var outputs = new List<Action<TOut>>(); foreach (var modifiable in modifiables) { inputs.Add(modifiable.Value); outputs.Add(modifiable.Setter); } var values = modifier(inputs.ToArray()); if (values.Count != outputs.Count) { throw new ArgumentException("The item count must not change.", nameof(modifier)); } for (int i = 0; i < values.Count; i++) { outputs[i](values[i]); } } } }[/code] Usage: [code]using System; using System.Collections.Generic; using System.Linq; using LinqBack; namespace LinqBack_Test { class Program { class World { public IEnumerable<Player> Players { get; set; } } class Player { public string Name { get; set; } public Data Data { get; set; } } class Data { public Position Position { get; set; } } class Position { public int X { get; set; } public int Y { get; set; } } static void ShuffleInPlace<T>(IList<T> list) { var r = new Random(); for (int i = 0; i < list.Count - 1; i++) { var i2 = r.Next(i, list.Count); var t = list[i]; list[i] = list[i2]; list[i2] = t; } } static void Main(string[] args) { #region terribleness var o = new { world = new World { Players = new[] { new Player { Name="Steve", Data = new Data { Position = new Position {X=0, Y=1} } }, new Player { Name = "Dave", Data = new Data { Position = new Position { X = 1, Y = 2 } } }, new Player { Name = "Bob", Data = new Data { Position = new Position { X = 2, Y = 3 } } }, new Player { Name = "Hank", Data = new Data { Position = new Position { X = 3, Y = 4 } } } } } }; #endregion o .world .Players .Select(player => player.Data) .SelectValueSetterPair<Data, Position, Position>(data => data.Position, data => value => data.Position = value) // I have no idea why this isn't inferred automatically. .ModifyAll(positions => { ShuffleInPlace(positions); return positions; }); o .world .Players .Select(player => player.Data) .SelectModifiable(data => data.Position) .ModifyAll(positions => { ShuffleInPlace(positions); return positions; }); } } }[/code] .NET normally doesn't offer any (good) interface for deep cloning (with the ones being there being either really slow or doing memory editing), so the library does in-place modification instead. Since it seems useful for one of my larger projects I'll probably continue this and maybe put it on Bitbucket eventually. If you want a library that makes a sparse clone instead you'd have to either compile that dynamically for each query variation (which is pretty cumbersome, but should be doable with a [I]CloningQueryable<TOuter, TValue, TSet>[/I] keeping track of the necessary info) or by providing an interface on the classes themselves (which would be fast without [I]Reflection.Emit[/I] but incredibly cumbersome to use). Alternatives that are definitely more inefficient with repeated queries, but compatible with LinqBack, are: - Full deep cloning (which is possible with reflection and [URL="https://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatterservices.getsafeuninitializedobject%28v=vs.110%29.aspx"][I]FormatterServices.GetSafeUninitializedObject[/I][/URL] or [URL="https://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatterservices.getuninitializedobject%28v=vs.110%29.aspx"][I]FormatterServices.GetUninitializedObject[/I][/URL]) - Deep cloning + deduplication ...but if you want to do that you really should just use [URL="https://msdn.microsoft.com/en-us/library/dd233184.aspx"]F# Records[/URL] instead and use that language's [URL="https://msdn.microsoft.com/en-us/library/dd548046.aspx"]statically resolved type parameters[/URL] to build your query functions. (I'm pretty sure you can access copy and update record expressions from generics with statically resolved type parameters at least. I'm not entirely sure though, I would just consider it being impossible unusual.) [editline]11th September 2015[/editline] I probably won't try to make a [I]CloningQueryable<TOuter, TValue, TSet>[/I] library for C# since I don't have a use for it, but [I]FormatterServices.GetUninitializedObject[/I] together with the APIs used in [I]SelectModifiable[/I] should be enough to make something reasonable I think.
[QUOTE=Tamschi;48661198]LinqBack library (incomplete): .NET normally doesn't offer any (good) interface for deep cloning (with the ones being there being either really slow or doing memory editing), so the library does in-place modification instead. Since it seems useful for one of my larger projects I'll probably continue this and maybe put it on Bitbucket eventually. If you want a library that makes a sparse clone instead you'd have to either compile that dynamically for each query variation (which is pretty cumbersome, but should be doable with a [I]CloningQueryable<TOuter, TValue, TSet>[/I] keeping track of the necessary info) or by providing an interface on the classes themselves (which would be fast without [I]Reflection.Emit[/I] but incredibly cumbersome to use). Alternatives that are definitely more inefficient with repeated queries, but compatible with LinqBack, are: - Full deep cloning (which is possible with reflection and [URL="https://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatterservices.getsafeuninitializedobject%28v=vs.110%29.aspx"][I]FormatterServices.GetSafeUninitializedObject[/I][/URL] or [URL="https://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatterservices.getuninitializedobject%28v=vs.110%29.aspx"][I]FormatterServices.GetUninitializedObject[/I][/URL]) - Deep cloning + deduplication ...but if you want to do that you really should just use [URL="https://msdn.microsoft.com/en-us/library/dd233184.aspx"]F# Records[/URL] instead and use that language's [URL="https://msdn.microsoft.com/en-us/library/dd548046.aspx"]statically resolved type parameters[/URL] to build your query functions. (I'm pretty sure you can access copy and update record expressions from generics with statically resolved type parameters at least. I'm not entirely sure though, I would just consider it being impossible unusual.) [editline]11th September 2015[/editline] I probably won't try to make a [I]CloningQueryable<TOuter, TValue, TSet>[/I] library for C# since I don't have a use for it, but [I]FormatterServices.GetUninitializedObject[/I] together with the APIs used in [I]SelectModifiable[/I] should be enough to make something reasonable I think.[/QUOTE] Pretty cool stuff. The difference in the approach is that my combinators start constructing the "pipeline" from the start so when you eventually do your modification it still knows what kind of object it started with so it can reconstruct one that's the same plus the modifications. And when you said lambdas can convert to Expressions I didn't think it's literally that simple. So you just wrap the thing into an Expression and you get access to the AST? That's really great. Another thing, I also get this lack of inference on one of my functions. Well... it does type check when I write the whole thing, but it doesn't offer IntelliSense while writing it. I wonder is the cause is the same.
[QUOTE=Darwin226;48661533]Pretty cool stuff. The difference in the approach is that my combinators start constructing the "pipeline" from the start so when you eventually do your modification it still knows what kind of object it started with so it can reconstruct one that's the same plus the modifications.[/QUOTE] Right. You can definitely store that state but it's somewhat cumbersome to set up and you can't write the extensions as a plain single generator method. There are a few shortcuts I use e.g. in my ReactiveX clone, like passing lambdas into a common wrapper method. I'd probably also restrict cloning to anything [I][Serializable][/I] because doing otherwise may be pretty unsafe. [QUOTE]And when you said lambdas can convert to Expressions I didn't think it's literally that simple. So you just wrap the thing into an Expression and you get access to the AST? That's really great.[/QUOTE] It's limited though. Anything that needs curly brackets generally is incompatible. [del][URL="https://msdn.microsoft.com/en-us/library/bb397687.aspx"]It seems I was mistaken about this.[/URL] I remember reading somewhere that there were some restrictions, but I have no idea which ones or if that's really (still) the case.[/del] [URL="https://facepunch.com/showthread.php?t=1483860&p=48666414&viewfull=1#post48666414"][editline]edit[/editline] See below.[/URL] It's also completely not typesafe (but you could make a wrapper library that provides those constraints). [QUOTE]Another thing, I also get this lack of inference on one of my functions. Well... it does type check when I write the whole thing, but it doesn't offer IntelliSense while writing it. I wonder is the cause is the same.[/QUOTE] The reason is probably the second lambda in that line. The type inference algorithms is normally decent but it has trouble with complicated cases and variant generic interfaces that are unconstrained but could be set to [I]object[/I] for the most general case.
I haven't done much programming in a very long time. So I finally some how found the will to start again and I have been writing a thing. I don't really know what it is going to be used in yet, maybe a game or something. I have a class called ObjectManager, it keeps a record of all object instances. It has a few methods to help manage objects as well. I got to thinking, if I wanted to draw images would I do it from my object manager class or should my objects themselves do it? I am doing something like this right now: [code]ObjectManager obj = new ObjectManager(); new Mouse(obj); // obj[0] new Rat(obj); // obj[1] obj.setAggression(1, 10); // set aggression of the rat to 10. while (obj.next()) { // iterates through an array of objects obj.step(); // calls objects step() function, eg class Rat extends Animal { step() { this.age += 0.1; }; } obj.draw(); // calls objects draw() function } [/code] But this doesn't seem like the [I]right way[/I] of doing it. I'm afraid I'm going to get stuck in the whole "if I can't do it the right way I won't do it at all" mind set and stop programming again. In it's simplest form, where would you guys manage stepping and drawing?
auto cells, nothing special, but neat [img]http://i.imgur.com/DWHpEJM.gif[/img] [img]http://i.imgur.com/FokknOA.gif[/img]
[vid]https://my.mixtape.moe/kmrumg.webm[/vid] ALMOST THERE BOYS
[QUOTE=false prophet;48661835] But this doesn't seem like the [I]right way[/I] of doing it. I'm afraid I'm going to get stuck in the whole "if I can't do it the right way I won't do it at all" mind set and stop programming again. In it's simplest form, where would you guys manage stepping and drawing?[/QUOTE] i think its a dangerous mindset to always try to do things "the right way" because for a lot of things, there probably is no "right way". the thing people might call "the right way" now was probably not "the right way" years ago. choose whatever works best for you and your project and you will be able to see the pros and cons of doing things a certain way and get more insight on what to do next time i think if people kept sticking to always doing things "the right way", nobody would ever find a better way and there would never be any progress or innovation
That's a good outlook on the problem. I'm going to try and adopt it.
Hell yeah! I got basic cubemap reflections working! [vid]https://my.mixtape.moe/xscnic.mp4[/vid] I thought it was going to be a bit more complex to implement, but then I realized I already made things that were needed for this, such as my [B]Specular mapping pass[/B] yesterday, and a Cubemap texture component for storing and using cubemap textures. So textures that supply a specular map will reflect a cubemap based on, well, how a specular map is read. Also I did this all by myself, no tutorials! :excited:
now with multithreads [img]http://i.imgur.com/undONIA.gif[/img]
[QUOTE=polkm;48663811]now with multithreads -gif-[/QUOTE] [video=youtube;w9JABxGCjxk]http://www.youtube.com/watch?v=w9JABxGCjxk[/video]
The human vocal tract is a very complicated resonator. [IMG]http://i.imgur.com/76xGZzE.png[/IMG] One of the higher frequencies is present only for half of the fundamental's phase. And this only appears in open vowels. I have no idea how I am going to start creating a mathematical model of this.
I'm in the middle of writing the documtation for managing the backend components of the project that I've been working on for my internship. The functional demo of the program is coming along nicely, and I'm pretty sure they're talking to investors. I wasn't involved with the brunt of the project but the components that I wrote are core to the whole system. (Which is think is really cool) Hopefully it goes public soon so I can show you all exactly what I've been up to!
Quick question because perhaps someone knows: [I][U]is phong lighting typically used in games?[/U][/I] I haven't hit the "limit" yet for the amount of shader operations I'm currently using, but am interested in implementing phong lighting to complement my current lighting operations only if it isn't extremely taxing and is kinda common place now a days. I would love to get some nice smooth reflections and lighting instead of these janky ones I get.
[QUOTE=Karmah;48664982]Quick question because perhaps someone knows: [I][U]is phong lighting typically used in games?[/U][/I] I haven't hit the "limit" yet for the amount of shader operations I'm currently using, but am interested in implementing phong lighting to complement my current lighting operations only if it isn't extremely taxing and is kinda common place now a days. I would love to get some nice smooth reflections and lighting instead of these janky ones I get.[/QUOTE] It's definitely more expensive. I wouldn't say one model is better than the other so if you can support it, go for it. It definitely looks better, and almost all modern game engines support or use Phong interpolation Or to answer your question: yes [editline]12th September 2015[/editline] Also note that a lot of modern engines are moving over to deferred rendering which makes Phong interpolation obsolete
Sorry, you need to Log In to post a reply to this thread.