Open Dominaezzz opened 5 years ago
With pleasure, however JitPack is enough for me at the moment
I know Bintray and Maven require going throw so much pain that makes me giving up.
But if you want to try, you are very welcome.. I sent you an invite :)
Ah sweet, I'll give it a try. I also just noticed it has a dependency on kotlin-unsigned
, which will have to go multi-platform too. Unless you're thinking of migrating to kotlin's unsigned types.
There is another problem you have to solve first. I think the way we handle native memory is dependent on lwjgl or at least dependent on java.nio
. I don't think there is a multiplatform version of any of those. I think it should be possible to move all those dependencies to the jvm version, but I'm not sure how much work this is actually.
Well, I just did a search for ByteBuffer
and it looks like there are a couple options. Migrate them to use kotlinx.io.core.IoBuffer
which is multi-platform (except for android native), abstract away the JVM specific bits into a separate jvm "extension" library or only implement the java.nio related parts in the JVM source set.
How much do you care about breaking changes?
I personally don't care. If we go multiplatform IoBuffer
seems like the best choice for the future even though it's still experimental.
As for breaking changes, I don't think they will be any problem.
Oh man, I lost the reply mail..
anyway @Dominaezzz, the limitation with the stdlib unsigned is that they dont extend Number
because inline classes cant extend classes, I explicitely asked to change Number
to interface, but my request was turned down because it didnt seem like an issue for them
Extending Number
is cool because you can pass it as generic and then converting to the type you want.
We may discuss if this is actually useful also for unsigned.. maybe we could switch to stdlib unsigned and make unsigned vectors somehow special compared to the others.
Regarding breaking changes, I personally repute that if the change makes sense, then we should just go for it
Although I'd pay attention for any regression performance-wise
any news, @Dominaezzz ?
I made a branch with some changes. Have you looked at it?
The current code is a bit of a tangle of interfaces, which is quite bothersome to make multiplatform. Would be easier to wait for io-2
or make the library more kotlin "friendly" (less like the C api). The former being easier.
I made a branch with some changes. Have you looked at it?
Yep, they are only confined to gradle or?
The current code is a bit of a tangle of interfaces, which is quite bothersome to make multiplatform. Would be easier to wait for
io-2
or make the library more kotlin "friendly" (less like the C api). The former being easier.
I'm totally available to change the current structure if it makes sense.
By io-2
do you mean kotlin.io
? is there already an ETA?
Yes, the new revamped kotlinx-io. It should be released sometime around Kotlin 1.4 release.
Oh, maybe I didn't push. Structural changes would be required, it's been a while so I don't remember what exactly needed to change. I just remember pulling interfaces into common code required pulling so many other things at once.
Also, dependencies would also need to be made multiplatform. unsigned won't work well with Kotlin native, there would be a fair bit of copying involved.
It's a bit of work.
Yes, the new revamped kotlinx-io. It should be released sometime around Kotlin 1.4 release.
Where is possible to read something about that?
Oh, maybe I didn't push. Structural changes would be required, it's been a while so I don't remember what exactly needed to change. I just remember pulling interfaces into common code required pulling so many other things at once.
What would you do then? Top functions inside the glm
package?
Also, dependencies would also need to be made multiplatform. unsigned won't work well with Kotlin native, there would be a fair bit of copying involved.
Problem with the stdlib unsigned is that they do not extend the Number
abstract class..
We could keep unsigned a jvm-only dep
Where is possible to read something about that?
The most official thing I can point to is the Kotlin 1.4
blog. Other places would be hand wavy estimates from Kotlin devs in Slack.
What would you do then? Top functions inside the
glm
package?
You would need to make a lot of the member functions into extension functions or top-level functions. This would make the library not very nice to use from Java. You could start moving the smaller interfaces into common code, I'm not very familiar with the library so I wouldn't know exactly where to start.
Problem with the stdlib unsigned is that they do not extend the
Number
abstract class.
I understand the need for this but using Number
means boxing very often. Also, I guess there isn't much Kotlin devs can do about it, it's a Java api. :man_shrugging:
You would need to make a lot of the member functions into extension functions or top-level functions. This would make the library not very nice to use from Java.
No other ways?
Otherwise we can theoretical have the top level functions as the real multiplatform core and the jvm interfaces would just point to those (maybe inlined)
You could start moving the smaller interfaces into common code, I'm not very familiar with the library so I wouldn't know exactly where to start.
Ok, let's start with the projection matrix functions, if you can prepare the structure and point me to the right location I'll move a couple of those
I understand the need for this but using
Number
means boxing very often. Also, I guess there isn't much Kotlin devs can do about it, it's a Java api. 🤷♂
Yeah, but the convenience is huge. Otherwise we shall write a lot of boilerplate code (or auto-generate)
Hi @Dominaezzz, still interested in this? From my side there is plenty of availability
I've been notified of some rising interest into multiplatform libraries for images and fonts (@Sylvyrfysh).
I'd love to merge all the spare efforts in an unique one.
Also, dependencies would also need to be made multiplatform. unsigned won't work well with Kotlin native, there would be a fair bit of copying involved.
It's a bit of work.
We can skip unsigned for the moment and postpone any decision/evaluation for later
You would need to make a lot of the member functions into extension functions or top-level functions. This would make the library not very nice to use from Java.
What is preferred among the two?
We can always add an additional layer/wrapper explicitely for Java to make it play nicely with it
You could start moving the smaller interfaces into common code, I'm not very familiar with the library so I wouldn't know exactly where to start.
We can start by the matrixClipSpace
extension with functions to create ortho and perspective projection matrices, they are first methods used in the simplest scenarios.
What do you think?
What is preferred among the two?
I think extension functions. (Might have to be on a case by case basis).
We can start by the matrixClipSpace extension with functions to create ortho and perspective projection matrices, they are first methods used in the simplest scenarios.
We could start there, after pulling out Mat4
into common.
still interested in this?
I am but I'm not available to do the work on it.
I think extension functions. (Might have to be on a case by case basis).
Ok, then extension functions be
We could start there, after pulling out
Mat4
into common.
Fine, but I have no idea where to start from
I am but I'm not available to do the work on it.
It's ok, I can do the part about writing the mathematical code (methods, classes and so on), but I have no experience in native/MP, that's your knowledge I guess
Could you take care of the project structure?
I just deleted everything in the multiplatform
branch, created a dummy Mat4
and a dummy ortho
What backends do you want, @Dominaezzz ? The code I've started working with is aimed at everything, do you also want everything or is a subset what you want?
Everything really, I don't expect this to require anything platform specific.
I gave it a refresh, @Dominaezzz
I started with the gradle build file, moved to kotlin script, followed the tutorial on the website removed the glm-test
module and all external dependencies except lwjgl ones
I have some problems understanding how to distinguish common code from the jvm specific one and how to set that in gradle
if you could help on that, it'd be great
Reading through the thread and seeing what I can do to help. Some thoughts:
We can forget about Java for the moment. If you need the methods as extension function, this is no problem at all.
However I can see a solution to that. We can define all our methods as extension functions as Dominic suggested and then I'll add an additional wrapper exclusively for the jvm, to make it play nice with Java (at least the core functions, already today there are stuff, like custom vector swizzle, which is basically kotlin-only).
My final goal is to have a library which helps with 3d real time graphics. Having something resembling the standard de-facto library on C/C++ world is a huge adavantage.
Number
(which is a technical obstacles for inline classes), I tried to ask Jetbrains for a switch and make it as an interface
, but I was told there were not enough usage cases for that. Hopefully they will change their mind if we ask it all together.The advantage of an unsigned extending Number
, is that, of course, one can have a generic method accepting it and then you convert it internally to what you need there.
But again, this is not a strict requirement
I'll send you an invitation :)
- the initial scope was that. But nothing stops us from extending it and adding new functionalities, like I myself did
We can forget about Java for the moment. If you need the methods as extension function, this is no problem at all.
However I can see a solution to that. We can define all our methods as extension functions as Dominic suggested and then I'll add an additional wrapper exclusively for the jvm, to make it play nice with Java (at least the core functions, already today there are stuff, like custom vector swizzle, which is basically kotlin-only).
That's a good approach, I think
My final goal is to have a library which helps with 3d real time graphics. Having something resembling the standard de-facto library on C/C++ world is a huge adavantage.
Definitely.
- the problem with the stdlib unsigned is that they do not extend the abstract class
Number
(which is a technical obstacles for inline classes), I tried to ask Jetbrains for a switch and make it as aninterface
, but I was told there were not enough usage cases for that. Hopefully they will change their mind if we ask it all together.The advantage of an unsigned extending
Number
, is that, of course, one can have a generic method accepting it and then you convert it internally to what you need there.But again, this is not a strict requirement
As Dominic mentioned above, using generics actually causes automatic boxing (ie, the storage type of Vec3 and Mat4 will end up being Object on the JVM, and possibly elsewhere). In order to avoid that we'd actually have to define the various vector and matrix types separately, and depending on how many there are, it might be best to do that through code generation using something like kotlinpoet. That would eliminate the need for the Number
extend.
I'll take a look what I can do about the Multiplatform build
As Dominic mentioned above, using generics actually causes automatic boxing (ie, the storage type of Vec3 and Mat4 will end up being Object on the JVM, and possibly elsewhere). In order to avoid that we'd actually have to define the various vector and matrix types separately, and depending on how many there are, it might be best to do that through code generation using something like kotlinpoet. That would eliminate the need for the
Number
extend.
Yep, that would do eliminate it
However it might be worth starting small with a manual writing of some classes and see how it plays on different platform before trying to scale up and use kotlinpoet (are you familiar with it though?)
definitely. yeah I've used Kotlin poet before with the kgl implementation, but I have to admit it's a pain to use codegen in general, but it's also a pain to have to write everything manually.
Yeah, which platform(s) in particular are you interested in?
I'm personally working with Kotlin/Native (Linux, macOS, and Windows), but with common code, it's trivial to run on any platform.
@elect86 what was the reason for choosing glm_
as a package name instead of simply glm
?
Clashes with the glm
object
I'm having difficulty getting a multiplatform configuration to compile because of java module shenanigans, probably caused by lwjgl. What exactly is the purpose of the lwjgl dependency?
EDIT: To be clear, the compiler is crashing for reasons unknown.
native vecs and mats uploads
you can skip it for the moment
So going through the code after removing the lwjgl dependency, a multiplatform port would basically require starting over from scratch. Every data type is written with memory management in mind, which is something that's platform-specific and should either be omitted, or offloaded to a platform-specific module (eg glm-jvm
, with extensions for lwjgl or something like that). This will not be easy.
@elect86 Because of how templates work in C++, I've set up the basic structure for codegen in the multiplatform branch and put in a basic Vec2 class generation. I haven't touched the travis or jitpack files, but they'll likely need to be updated or replaced for multiplatform CI (I'm not very knowledgable about that)
Generation code looks pretty clear and logic, I like it
So going through the code after removing the lwjgl dependency, a multiplatform port would basically require starting over from scratch. Every data type is written with memory management in mind, which is something that's platform-specific and should either be omitted, or offloaded to a platform-specific module (eg
glm-jvm
, with extensions for lwjgl or something like that). This will not be easy.
are vecs and mats backed by primitive arrays fine in native?
@elect86 Because of how templates work in C++, I've set up the basic structure for codegen in the multiplatform branch and put in a basic Vec2 class generation. I haven't touched the travis or jitpack files, but they'll likely need to be updated or replaced for multiplatform CI (I'm not very knowledgable about that)
I can handle those later
Shall we offload to code generation also Glm
tied functions like these? I think it would make sense to have Float
, Double
for the corresponding floating point matrix. Shall we consider offering an alternative accepting Int
s?
Extension function means something like, for example:
fun Glm.frustum (T left, T right, T bottom, T top, T near, T far)
Is this ok for you, guys?
are vecs and mats backed by primitive arrays fine in native?
They are preferable. primitive arrays can be pinned and sent to C functions as pointers, which basically every C function expects in some way. Exposing the inner storage through an asFloatArray()
function also allows them to be mutated from a C function, just like in C++.
Shall we offload to code generation also Glm tied functions like these? I think it would make sense to have Float, Double for the corresponding floating point matrix. Shall we consider offering an alternative accepting Ints?
That's a good question: should those functions be put into the artificial namespace I feel like extension functions to the Glm
, or should they just be top-level functions?Glm
object are probably a good solution.
In terms of types, glm actually defines only Float, Double, Boolean, Int, and UInt vectors, and many functions are specific to floating point types. That should be reflected in the API for this library. The goal should be feature parity, but in a Kotlinic way, with as much API mirroring as reasonable.
It's also a question whether the Kotlin port needs to keep the extremely short type names, since they're not kotlinic at all. I would propose using FloatVec2, IntVec2, etc, FloatMat3, FloatMat3x2, FloatMat4, etc as a compromise between conciseness and clarity.
EDIT: On a related note, while many collection types in Kotlin have mutable and immutable variants, vectors work much like arrays, which even in Kotlin are always mutable.
They are preferable. primitive arrays can be pinned and sent to C functions as pointers, which basically every C function expects in some way. Exposing the inner storage through an
asFloatArray()
function also allows them to be mutated from a C function, just like in C++.
Excellent, this is what I like as well
~That's a good question: should those functions be put into the artificial namespace
Glm
, or should they just be top-level functions?~ I feel like extension functions to theGlm
object are probably a good solution.
Ok
In terms of types, glm actually defines only Float, Double, Boolean, Int, and UInt vectors, and many functions are specific to floating point types. That should be reflected in the API for this library. The goal should be feature parity, but in a Kotlinic way, with as much API mirroring as reasonable.
It makes sense
It's also a question whether the Kotlin port needs to keep the extremely short type names, since they're not kotlinic at all. I would propose using FloatVec2, IntVec2, etc, FloatMat3, FloatMat3x2, FloatMat4, etc as a compromise between conciseness and clarity.
Maybe Float2, Int2 for vectors? This wouldn't however work very well with matrices, although I never saw the needs beyond floating point types..
EDIT: On a related note, while many collection types in Kotlin have mutable and immutable variants, vectors work much like arrays, which even in Kotlin are always mutable.
we may use view interfaces, that would allow the values to be simply retrieved
Maybe Float2, Int2 for vectors? This wouldn't however work very well with matrices, although I never saw the needs beyond floating point types..
Float2, Int2, etc are established ways to name vectors in the industry (cf DirectX and Metal), but doesn't fit with GLM, which uses the vec<2, double, …>
convention. Given that GLM's types are defined as vec2d
, I think DoubleVec2
might be a better Kotlin translation (given the goal of GLM familiarity). Doesn't matter to me personally, but it should be consistent.
EDIT: Matrices would be named as Float4x4
, Float3x4
, etc, and GLM only defines matrix types for floating point values (Float and Double).
we may use view interfaces, that would allow the values to be simply retrieved
What I'm saying is that I don't think we need immutable types since vectors are basically fixed-length arrays with linear algebra logic built in, so I don't think view interfaces are necessary either.
Float2, Int2, etc are established ways to name vectors in the industry (cf DirectX and Metal), but doesn't fit with GLM, which uses the
vec<2, double, …>
convention. Given that GLM's types are defined asvec2d
, I thinkDoubleVec2
might be a better Kotlin translation (given the goal of GLM familiarity). Doesn't matter to me personally, but it should be consistent.EDIT: Matrices would be named as
Float4x4
,Float3x4
, etc, and GLM only defines matrix types for floating point values (Float and Double).
We can start then like that.
What I'm saying is that I don't think we need immutable types since vectors are basically fixed-length arrays with linear algebra logic built in, so I don't think view interfaces are necessary either.
ok
Do you happen to know how to generate methods comments?
So there's an addKdoc
function for FunSpec.Builder and I imagine for class builders as well
Glm
object generated as well? If yes, then I don't know how to reference it for the function extensionsVec2
generation into a spare top function and create a Mat4
equivalent.. do we want to have just a single task (that is GenerateGLM
) to generate everything?Utils
class.. where is from? Ps: I just added another method (val
) to generate class properties that follow the same order as you would normally write code (mutable modifier and then name, type and initialiazer), I find it much more intuitivestorage
to be private? Letting it public might be useful in some scenarios where you want to save/load the whole matrix at onceobject Glm
I feel like should just be an empty declaration akin to a namespace. All the functions can then be extension functions on Glm
. It certainly doesn't have to be generated, an in general not everything needs to be generated. Generation is really only necessary in places where there would be a large amount of code duplication, like for the various vector types, or all the different versions of functions for the different parameter types that can be passed in. I wonder if one could write a generic function and then generate code based on a list of types, kind of like C++ templates or macros, but at build time... 🤔 GenerateGLM
task, so you don't have everything in one function.val
property was already added using the property
builder. I wrote the builder functions (why they're not part of the KotlinPoet library already I can't tell you), but I don't care what things are called. Just make sure you're not adding functionality that's already there and we don't end up with two different ways to do the same thing.storage
property would be private, but readable with an asFloatArray()
, asIntArray()
, etcI wonder if one could write a generic function and then generate code based on a list of types, kind of like C++ templates or macros, but at build time... thinking
It is currently possible to do this actually. We can write a Kotlin compiler plugin to convert generic types to multiple specific types.
class Vector2<T>(val x: T, val y: T, val z: T)
@Reify
typealias Vector2f = Vector2<Float>
@Reify
typealias Vector2d = Vector2<Double>
after building with our Reification plugin,
class Vector2<T>(val x: T, val y: T, val z: T)
class Vector2f(val x: Float, val y: Float, val z: Float)
class Vector2d(val x: Double, val y: Double, val z: Double)
Example of compiler plugin in action -> kotlin-power-assert
That is fascinating... I'm not sure what it would look like to try and implement functions that can take a different generic type than the class though. for example:
fun <T, U> Vector2<T>.plus(scalar: U): Vector2<T>
Perhaps a parameter on the Reifiy annotation? I feel like in many ways this would make coding easier, but I'm not sure if it's a rabbit hole worth going down 🤔
And then there'd need to be an option for the compiler to remove the generic version entirely (or perhaps a sealed class hierarchy is able to achieve what's needed there)
Lots of interesting discussions here.
Just wondering if any thoughts have been put into, rather than doing a major reworking of the entire library for Multiplatform use, doing it piece by piece and start with simply splitting the interfaces into basic ones and more full featured (JVM-only ones)?
So that for any ones that currently include JVM-specific functionality they each extend a second Platform{InterfaceName}
interface and have this declared with expect interface
in the commonMain
and actual interface
with separate interfaces for JVM (with just the JVM-specific code) and native (simple empty interfaces)
I would be perfectly fine having some of the ByteBuffer, InputStream functions etc be JVM only to begin with. Then platform specific implementations could be added over time to bring the full multiplatform library up to par with the JVM-only parts?
Lots of interesting discussions here.
Just wondering if any thoughts have been put into, rather than doing a major reworking of the entire library for Multiplatform use, doing it piece by piece and start with simply splitting the interfaces into basic ones and more full featured (JVM-only ones)?
@nlbuescher started writing down something for code generation, I also wrote something in this direction, especially on the DSL side, but I don't have much time. Also I wonder if the approach you mention makes actually more sense at the very moment.
So that for any ones that currently include JVM-specific functionality they each extend a second
Platform{InterfaceName}
interface and have this declared withexpect interface
in thecommonMain
andactual interface
with separate interfaces for JVM (with just the JVM-specific code) and native (simple empty interfaces)I would be perfectly fine having some of the ByteBuffer, InputStream functions etc be JVM only to begin with. Then platform specific implementations could be added over time to bring the full multiplatform library up to par with the JVM-only parts?
Yeah, I totally agree with
I'd send you an invitation in case you are interested in some contributions
Hi all,
also currently trying to use this in a multiplatform project, but one that has only JVM and common platforms. One thing I've been experimenting with is similar to what AndreasMattsson
suggested; creating "dummy" expect classes that have all implementation and jvm specific code stripped away, and then putting actual classes in a JVM module with actual declarations for each method. apart from some teething issues with kotlin's type system (losing type information when function bodies are removed), it's pretty straightforward and only requires someone to go through and manually refactor all of the source files.
This will only work for JVM platforms, but it can be extended to add support for native platforms down the line
For my purposes, this is ideal, as the common code using these matrixes doesn't need any of the java dependant features
A note: I'm experimenting using the stdlib unsigned for the moment
First multiplatform test (jvm + linux) passing
I want to use this library in a multiplatform application. Since the bulk of this library is written in pure Kotlin, going multiplatform would be fairly trivial. The only possible issue I can think of is that JitPack may not work with it, breaking current users. Would need to setup build for all platforms to publish to Bintray or Maven central.