idmillington / cyclone-physics Goto Github PK
View Code? Open in Web Editor NEWThe Physics engine that accompanies the book "Game Physics Engine Design"
License: MIT License
The Physics engine that accompanies the book "Game Physics Engine Design"
License: MIT License
Sorry...I have been staring at this too long, and thought I would ask for help.
quaternion.rotateByVector is not having the effect that I am expecting.
Can someone post simple examples:
setOrientation(); --> Is default orientation along the x-axis? (I assume yes: If I use getAxisVector(0), I get (1,0,0))
Hey,
There's a small typo in pcontacts.cpp. Change "acelleration" to "acceleration" on line 52.
Thanks,
Matt
At line : https://github.com/idmillington/cyclone-physics/blob/master/include/cyclone/collide_coarse.h#L268
Children's parent is not updated, so the tree is no more valid. The code should be :
parent->children[0] = sibling->children[0];
if (parent->children[0]) parent->children[0]->parent = parent;
parent->children[1] = sibling->children[1];
if (parent->children[1]) parent->children[1]->parent = parent;
Hey guys, I'm very noob at this. Could someone please help me?
I downloaded the project, tried to run it, but then this happens:
"Unable to start program C:\cyclone-physics-master\build...\lib\cyclone.lib"
What's going on? Could someone help please?
Hello, I am a new student in Computer Science. I can't run the project I download here. It says that " cannot open source file 'cyclone/body.h' " and I didn't know why. Can anyone help me to solve this problem? I use Window 10 and 64 systems.
There are issues in the code that interact badly with the new Visual Studio 2010 compiler. I don't have this compiler yet, so if anyone can provide me with a compile log, that would be great.
Apologies, not sure where else to ask this. I've been following the book for implementing a physics engine and looking at the source code here for reference.
There seems to be a problem with the calculation for FrictionImpulse. It seems to work absolutely fine until bodies with restitution are introduced. Any contact with a coefficient of restitution greater than 0.5 is guaranteed to cause explosive forces, and it's highly likely for values lower than that too. Often the objects just disappear out of existence due to such high changes in velocity.
I believe the problems come from the dynamic friction calculations and the use of the desired delta velocity (only var which is influenced by restitution).
Has anyone come up with a solution to this sort of thing? Thanks.
although I found nobody to call this method.
Considering applyImpulse is not called by anything else (that i could see) my guess is it's an obsolete function from an older version.
Hello Ian, I'm reading your book and am currently wondering how the variable accCausedSepVelocity can be a real instead of a vec3 in this snippet:
// Check the velocity build-up due to acceleration only
Vector3 accCausedVelocity = particle[0]->getAcceleration();
if (particle[1]) accCausedVelocity -= particle[1]->getAcceleration();
real accCausedSepVelocity = accCausedVelocity * contactNormal * duration;
You use it as a real in the rest of the function but it is computed from two vec3's multiplied with a duration, so I'm confused. Is the "*" operator a dot operator in disguise?
Thanks!
Seems that the timing library is the only code the has some SO specificity, as there used to be no portable way to fetch the time in a precise manner.
But, by using the chronos library of C++11 should be possible to do so. I have implemented a simple version of timing.{cpp|h}, using such library. I can compile and run correctly on linux, but do I cannot test this code on windows, and I do not know how C++11 support on windows is advancing. Would you kindly try the code?
The code can be found here: https://github.com/LucasCampos/cyclone-physics/tree/linux-c++11
I also would like to apologize in case I should send this a pull request rather than as a issue. If it is the case, please warn me.
All tests were done a Ubuntu 12.04 64bits, using g++ 4.6.3.
While trying to implement your system, it came to my attention that if there are two BVHNode
s that don't collide, it won't check collisions between children.
As far as I'm aware that means that if the two root's children aren't colliding, then no more collisions will be tested amongst objects deeper in the tree. Upon implementing this system, I have found that hardly any collisions are generated because they are all occurring within children deep down in the tree that are never being checked.
This article on a Dynamic AABB Tree has a different algorithm that I am about to compare to see if it fixes my issue:
http://allenchou.net/2014/02/game-physics-broadphase-dynamic-aabb-tree/
Hopefully you can point me towards some information as to whether this is a bug or just my misunderstanding.
According to the code above https://github.com/idmillington/cyclone-physics/blob/master/src/collide_fine.cpp#L623 the closestPt will be equal to relCentre if the center of the sphere is inside the box, which leads to a normal with zero length. Then you make a coordinate system with the zero normal and get a degenerated matrix with NaNs. The NaNs spread into every single part of the simulation and mess it up entirely.
I understand BVH and the code to create the hierarchy and I understand the code to insert nodes in the BVH but I don't understand how to insert bodies in the hierarchy. I mean I have a list with many bodies and I want to create the BVH.
To insert bodies is it enought with the follow?
BVHNode root;
for (int i=0; i<bodies.length; i++){
RigidBody currentBody = bodies.get(i);
root.insert(currentBody, volume);
}
The question is, to create the BVH is it engouth with create a node and iterate over all bodies and insert the bodies in the created BVH node (root)?
Note: I have used a java syntax.
https://github.com/idmillington/cyclone-physics/blob/master/include/cyclone/collide_coarse.h#L291
Should be
delete children[1];
instead of
delete children[0];
The method calculateContactBasis() of The contacts.cpp has a purpose of calculating orthonormal basis in contact coordinates.
So, I read the contents that the contact normal will be used as a X-axis in the contact coordinates.
To calculate the Z-axis and Y-axis in the contact coordinates, the code uses cross product first between X-axis(contact normal) and World-Up(0,1,0) Vector. and the result is Z-axis.
With the result, it calculates the Y-axis, using the cross product between Z-axis and X-axis.
So, the preliminary code of makeOrthonormalBasis in the book is like:
void makeOrthonormalBasis(const Vector& x, Vector* y, Vector* z)
{
// Calculate z from the vector product of x and y.
z = (*x) % (*y);
// Check for y and z in parallel
if(z->squaredMagnitude() == 0.0) return;
// Calculate y from the vector product of z and x
(*y) = (*z) % x;
// Normalize the output vectors
y->normalize();
z->normalize();
}
I think this method is right and the calculateContactBasis() is based on this logic.
// Scaling factor to ensure the results are normalised
const real s = (real)1.0f/real_sqrt(contactNormal.z*contactNormal.z +
contactNormal.x*contactNormal.x);
// The new X-axis is at right angles to the world Y-axis
contactTangent[0].x = contactNormal.z*s;
contactTangent[0].y = 0;
contactTangent[0].z = -contactNormal.x*s;
// The new Y-axis is at right angles to the new X- and Z- axes
contactTangent[1].x = contactNormal.y*contactTangent[0].x;
contactTangent[1].y = contactNormal.z*contactTangent[0].x -
contactNormal.x*contactTangent[0].z;
contactTangent[1].z = -contactNormal.y*contactTangent[0].x;
And My questions start here.
Because
Z-axis = crossProduct(X-axis, Y-axis)
if X-axis = (x1, x2, x3), Y-axis = (y1, y2, y3) = (0, 1, 0)
crossProduct(X-axis, Y-axis) =
| i j k |
| x1 x2 x3 |
| 0 1 0 |
= (x2 * 0 - x3 * 1, x1 * 0 - x3 * 0, x1 * 1 - x2 * 0)
= (-x3, 0, x1)
But the code is..
contactTangent[0].x = contactNormal.z*s;
contactTangent[0].y = 0;
contactTangent[0].z = -contactNormal.x*s;
When we just write the cross product between Z-axis and X-axis
Y-axis = crossProduct(Z-axis, X-axis)
if Z-axis = (z1, z2, z3) = (z1, 0, z3) (the calculated one from the cross product between X and Y axis,
X-axis = (x1, x2, x3)
crossProduct(Z-axis, X-axis) =
| i j k |
| z1 0 z3 |
| x1 x2 x3 |
= (0 * x3 - z3 * x2, -(z1 * x3 - z3 * x1), z1 * x2 - 0 * x1 )
= (-z3 * x2, - z1 * x3 + z3 * x1, z1 * x2)
We get the above equation. However, the code of calculating Y-axis is
contactTangent[1].x = contactNormal.y*contactTangent[0].x;
contactTangent[1].y = contactNormal.z*contactTangent[0].x - contactNormal.x*contactTangent[0].z;
contactTangent[1].z = -contactNormal.y*contactTangent[0].x;
The code should be like this
contactTangent[1].x = - contactNormal.y * contactTangent[0].z;
contactTangent[1].y = -contactNormal.z * contactTangent[0].x + contactNormal.x * contactTangent[0].z;
contactTangent[1].z = contactNormal.y * contactTangent[0].x;
In summary, the questions are:
Line 175 in faf5da1
Line 176 in faf5da1
Shouldn't we first multiply by a drag value (lines 175, 176) before calculating the new total velocity and rotation? The book (page 211) states that total rotation equals to:
R` = R * (damping)^t + At,
where R` - new total rotation, R - previous rotation, A - angular acceleration, t - delta time.
But now it looks more like:
R` = (R + At) * (damping)^t = R * (damping)^t + At * (damping)^t.
Sorry if some of these are reported. Will update if/when I find more.
contacts.cpp 516, 525
setIterations(iterations, iterations);
setIterations(velocityIterations);
should be
setIterations(iterations);
setIterations(velocityIterations, positionIterations);
contacts.h 346, 347 (tautology)
positionEpsilon >= 0.f && positionEpsilon >= 0.f
contacts.h 319 (unused variable)
bool validSettings;
collide_coarse.h 291 (suspicious delete)
delete children[1];
collide_fine.cpp 685-687 (calculation overwritten)
contact->contactPoint = plane.direction;
contact->contactPoint *= (vertexDistance-plane.offset);
contact->contactPoint = vertexPos;
resolveVelocity calculates impulse using the sum of the inverse mass of two particles:
real totalInverseMass = particle[0]->getInverseMass();
if (particle[1]) totalInverseMass += particle[1]->getInverseMass();
This is not correct because (1 / x) + (1 / y) != (1 / (x + y)). A more correct implementation would be:
real totalInverseMass = 1.0 / particle[0]->getInverseMass();
if (particle[1]) totalInverseMass += 1.0 / particle[1]->getInverseMass();
totalInverseMass = 1.0 / totalInverseMass
Why does the calculateDesiredDeltaVelocity https://github.com/idmillington/cyclone-physics/blob/master/src/contacts.cpp#L148
only use the x component of the contact velocity for calculating the desired velocity?
Specifically this code here
if (real_abs(contactVelocity.x) < velocityLimit)
{
thisRestitution = (real)0.0f;
}
// Combine the bounce velocity with the removed
// acceleration velocity.
desiredDeltaVelocity =
-contactVelocity.x
-thisRestitution * (contactVelocity.x - velocityFromAcc);
There is some weird behaviour where in the BigBallistics demo, where blocks sometimes seem to "absorb" shots without being moved and I suspect this is the cause.
This isn't a bug in the source code (as there is no source for Chapter 18), but I think there might be a mistake in one of the formulas on page 468 (2nd Edition), where you derive the formula for adding a scaled version of a rotation value to the orientation spinor.
Correct me if I'm wrong but, I think you just forgot to include t (duration) in this line and the line above it. The source code on page 470 seems to be consistent with this because you pass in duration to orientation.addRotation().
-Nolan
In plinks.cpp at line 45 for example there is always going to be a negative number created for penetration. This means that in the file pcontacts.cpp line 98 this condition is always true since we check if penetration is less than 0. So we dont get any interpenetration resolution for contacts since it always stops there.
Cant tell exactly what is wrong, because when i edit it myself the engine gets wierd behaviour when trying to resolve interpenetration. This means something else might be wrong after this line too.
I'm using the getRotation() and setRotation() for a rigid body and I noticed that these functions weren't returning an orientation like the comments suggested. Instead of orientation these functions seem to be about angular velocity. Changing the function name or comments in the body header (body.h) would be nice for future users.
In my opinion the calculation of the buoyancy is incorrect (file pfgen.cpp lines 120 and 121).
I think the correct calculation should look like this:
force.y = liquidDensity * volume *
(depth - maxDepth - waterHeight) / (2 * maxDepth);
Current code is missing the parentheses around "2 * maxDepth"
To correctly compile any demo, I needed to include cstddef on collide_coarse.h. Otherwise, the compilation fails with a lot of "'NULL’ was not declared in this scope" errors.
I'm using g++ version 4.6.3 on a Ubuntu 12.04.
Reading the book Game Physics Engine Development, in body.cpp:
The method _transformInertiaTensor() its confusing me: first, the 2º parameter q (its a quaternion), isn't used inside.
Second and more important: if I understand the code, this method executes the matrix operation MR_I(MI)_I(MR):
where MR is the rotation matrix, MI is the inertial tensor matrix, and I(x) is the inverse of x.
This is the traditional operation on matrix basis change...Ok when are translations in scene... but here only need the rotations! MR is in fact the entire transformation matrix (rotation + translation), but inside method the translation components of the MR are ignored: no translations involved. In this scenery..., using only rotation components of MR... i'm wrong or MR_I(MI)_I(MR) = I(MI)?? I test the function with diverse values...and confirm it! Or perhaps I miss some points and this is only true in some cases?
Can someone confirm if I'm correct? Thanks, and sorry for the poor english! :)
https://github.com/idmillington/cyclone-physics/blob/master/include/cyclone/core.h#L449
x = -z should be z = -z
I see code use this form to cauculate w
w =angular Velocity in world space
τ = torque in world space
α = angular Acceleration in world space
I = R * local_I * R^-1
τ = I α
w = w + αΔt
(when no τ , then no change of w)
I think this is Because when the object is not deformed, the "local_I" will not change.
But I find that on wiki (L =angular momentum)
L =I w , τ=ΔL/Δt
so if "I" is const, then
τ=ΔL/Δt
τ=Δ(Iw)/Δt
τ=IΔ(w)/Δt
τ= I α
but "I = R * local_I * R^-1" varies every frame, "I" is not const.
Is this because in one frame,we can considered "I" is const ? (Problem 1)
And my question is why not using the form /
ΔL = Δt τ
L = L+ΔL
L = I w
(when no τ, w may be diffrent because of I is not a const)
is this because this form make "w" not change continually ? (Problem 2)
Hi, I've tried to implement the engine (the Rigid Body one) inside my "Game Engine" sort, though I have a minor issue with the Collision Box component!
For some strange reason, I have this behavior if I rotate the cube before letting it fall to the ground!
Does anyone have some clue about what is happening? I double-checked the entire implementation of the engine and everything looks correct to me!
I don't need the actual solution, I just need a possible explanation of what is happening.
In the book (p 47) it says the inverse mass is used for representing infinite mass
.. objects of infinite mass have a zero inverse mass
but the code in particle.cpp reads
bool Particle::hasFiniteMass() const
{
return inverseMass >= 0.0f;
}
causing the bug
setInverseMass(0); hasFiniteMass() == true
Hello guys, I have two questions.
First of all, is there anyway to build this example in visual studio 2015? I downloaded the example and find build folder and executed sln files. However there is only vs2005 and vs2010 so I'm curious if there is a vs2015 version for this example.
Secondly, if there is no way to compile and build in vs2015, I'm going to uninstall vs2015 and install vs2010. I would like to know detailed manual for compiling / building examples in vs2010. (Like setting linking or something)
Give me a hand guys!
I cant run the flightsim:
1>------ Build started: Project: demo_flightsim, Configuration: Debug Win32 ------
1>Linking...
1>LIBCMT.lib(invarg.obj) : error LNK2005: __initp_misc_invarg already defined in LIBCMTD.lib(invarg.obj)
1>LIBCMT.lib(invarg.obj) : error LNK2005: __invoke_watson already defined in LIBCMTD.lib(invarg.obj)
1>LIBCMT.lib(invarg.obj) : error LNK2005: __set_invalid_parameter_handler already defined in LIBCMTD.lib(invarg.obj)
1>LIBCMT.lib(invarg.obj) : error LNK2005: __get_invalid_parameter_handler already defined in LIBCMTD.lib(invarg.obj)
1>LIBCMT.lib(invarg.obj) : error LNK2005: "void __cdecl _invoke_watson(unsigned short const *,unsigned short const *,unsigned short const *,unsigned int,unsigned int)" (?_invoke_watson@@YAXPBG00II@Z) already defined in LIBCMTD.lib(invarg.obj)
1>LIBCMT.lib(invarg.obj) : error LNK2005: __invalid_parameter already defined in LIBCMTD.lib(invarg.obj)
1>LIBCMT.lib(invarg.obj) : error LNK2005: "void __cdecl _invalid_parameter(unsigned short const *,unsigned short const *,unsigned short const *,unsigned int,unsigned int)" (?_invalid_parameter@@YAXPBG00II@Z) already defined in LIBCMTD.lib(invarg.obj)
1>LIBCMT.lib(invarg.obj) : error LNK2005: ___pInvalidArgHandler already defined in LIBCMTD.lib(invarg.obj)
1>LINK : warning LNK4098: defaultlib 'LIBCMT' conflicts with use of other libs; use /NODEFAULTLIB:library
1>..\bin/flightsim_d.exe : fatal error LNK1169: one or more multiply defined symbols found
1>Build log was saved at "file://c:\Documents and Settings\jpetrovic\Desktop\idmillington-cyclone-physics-1686718\tmp\flightsim\Debug\BuildLog.htm"
1>demo_flightsim - 9 error(s), 1 warning(s)
========== Build: 0 succeeded, 1 failed, 1 up-to-date, 0 skipped ==========
thankx for help!
For calculating the projection of Box on to an axis, I tried this method that I found in
cyclone/src/collide_fine.cpp
static inline real transformToAxis(
const CollisionBox &box,
const Vector3 &axis
)
{
return
box.halfSize.x * real_abs(axis * box.getAxis(0)) +
box.halfSize.y * real_abs(axis * box.getAxis(1)) +
box.halfSize.z * real_abs(axis * box.getAxis(2));
}
But it is not giving me correct results.
I made a small modification by taking absolute values of axis and box axis,
and that gives me correct results.
Can some one please verify this? I am not sure if I am doing something wrong elsewhere in my code.
in function void ParticleContact::resolveInterpenetration(real duration) (pcontacts.cpp around line 100)
you calculate total inverse mass like this
// The movement of each object is based on their inverse mass, so
// total that.
real totalInverseMass = particle[0]->getInverseMass();
if (particle[1]) totalInverseMass += particle[1]->getInverseMass();
I think this is mathematically wrong since 1/mass1 + 1/mass2 is not the same as 1/(mass1+mass2) as I think you mean. Browsing through the code I saw the same calculation in a number of points.
This line
https://github.com/idmillington/cyclone-physics/blob/master/src/fgen.cpp#L106
magnitude = real_abs(magnitude - restLength);
should be
magnitude = magnitude - restLength;
The force should become repulsive when magnitude is less than restLength. Consistently with the book.
Says "Unable to start the program C:/.../cyclone_d.lib" and "C:/.../cyclone_d.lib is not a valid Win32 application".
Solutions?
one of positionEpsilon
should probably be velocityEpsilon
/**
* Returns true if the resolver has valid settings and is ready to go.
*/
bool isValid()
{
return (velocityIterations > 0) &&
(positionIterations > 0) &&
(positionEpsilon >= 0.0f) &&
(positionEpsilon >= 0.0f);
}
First, it's more a question from c++ beginner, not a big report or something, not sure if this is acceptable, but please let me give it a shot :)
In core.h, there's almost every definition of class member functions, except for something like Matrix4::getDeterminant
or Matrix4::setInverse
functions.
I learned "everything in header" approach make member functions implicitly "inline",
BUT that "inline" means nothing about optimization, but just a sign that let multiple definition of a member function exist.
So I wish someone could let me know about:
core.h
?Matrix4::getDeterminant
andcyclone::getSleepEpsilon
, which is short function, in separated in .cpp file, while Matrix4::operator*
, which is long function, is in header?Following the issue 11 , I have a question regarding formula 6.5
In the book, Formula 6.5 stands for
As issue issue 11 says, there is a t missing in the formula, so it should be
But, if this formula comes, as I suspect, from uniform acceleration motion, since where are guessing that during all the frame we are applying the same acceleration to get the particle exactly to the position we want,
Are we missing the 2 factor as well in the final formula, getting the following formula as the corrected 6.5?
Line 177 in d75c8d9
// Calculate the resulting acceleration and therefore the force
Vector3 accel = (target - position) * (1.0f / duration*duration) -
particle->getVelocity() * duration;
Shouldn't that be:
// Calculate the resulting acceleration and therefore the force
Vector3 accel = (target - position) * (1.0f / duration*duration) -
particle->getVelocity() * (1.0f / duration);
While "release" mode is completely correct, When in "debug" build mode, the property settings for the project "demo_flightsim" are wrong:
Under: Configuration Properties / Linker / Input / Additional Dependencies:
It currently speficies: cyclone.lib
However it should specify: cyclone_d.lib.
Without that setting, the it doesn't link correctly since one uses Multi-Thread Debug and the other uses Multi-Threaded runtimes.
2 and 3)
If you go to the mode selector, ( the drop down for debug or release) and pick the configuration manager, you will see that 1/2 of the projects are not configured to compile ( Checkbox on the right) Without that setting, if you try to run those projects, it doesn't build them first, so you have to build them, then run them, if you forget, u will run stale code. (that is true for both debug and release.)
I've attached pictures of the settings.
These settings are confusing because visual studio has Configuration settings at the solution level as well as at the project level, and the settings are specified in the Configuration Manager.
Thanks
~Mark
The code in Contact::calculateDesiredDeltaVelocity looks off to me. The velocity from acceleration in body[0] appears to be ignored. The following snippet of code appears to have no effect:
if (body[0]->getAwake())
{
body[0]->getLastFrameAcceleration() * duration * contactNormal;
}
I'm guessing it's supposed to be:
if (body[0]->getAwake())
{
velocityFromAcc += body[0]->getLastFrameAcceleration() * duration * contactNormal;
}
https://github.com/idmillington/cyclone-physics/blob/master/src/collide_fine.cpp
Vector3 toSt, cOne, cTwo; real dpStaOne, dpStaTwo, dpOneTwo, smOne, smTwo; real denom, mua, mub;
If someone could clear up what each of these variables represents it would be much appreciated. I wish to understand.
I'm a student and I'm learning to make physics engine for adding my own game engine from your book these days. so I'm very pleased to submit this. and I hope you to reply my opinion whether good or bad.
I think that getPosition() can be better to take another function name in particle class, as you named fillGLArray(* param) function in Matrix4 class.
..
void getPosition(Vector *position) const;
..
void getPosition() const;
can be better to take the name below.
void fillPosition(Vector *position); const
I confused at first glance why that two functions had same name when I'm studying in this book actually.
so I changed that name 2 months ago in my engine. and now I'm studying at 305 page.
It was hard to get your book in Korea because Korean doesn't tend to make own physics engine, so there aren't translated version of this book. T.T
sorry that I'm not good at English. :<
Thank you !
Stack overflow within Matrix4::setInverse()
File: ./src/core.cpp
Line: 64, 68, 70-72
You've declared the data member of Matrix4 to be 12 elements, but refer to a 15th element in the inverse function. It is incorrect in the latest git clone and the book.
Chenz
please, why add these code for impulseContact.x in dynamic friction:
impulseContact.x = deltaVelocity.data[0] +
deltaVelocity.data[1]_friction_impulseContact.y +
deltaVelocity.data[2]_friction_impulseContact.z;
impulseContact.x = desiredDeltaVelocity / impulseContact.x;
how derive it?
why not use impulseContact directly?
Anyone knows? thanks.
The is missing implementation for declared class, why?
DELETE THIS
In code.cpp
const Vector3 Vector3::X = Vector3(0, 1, 0);
const Vector3 Vector3::Y = Vector3(1, 0, 0);
const Vector3 Vector3::Z = Vector3(0, 0, 1);
should be
const Vector3 Vector3::X = Vector3(1, 0, 0);
const Vector3 Vector3::Y = Vector3(0, 1, 0);
const Vector3 Vector3::Z = Vector3(0, 0, 1);
for consistency with the book
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.